简体   繁体   English

O(log n)编程

[英]O(log n) Programming

I am trying to prepare for a contest but my program speed is always dreadfully slow as I use O(n). 我正在努力为比赛做准备,但是当我使用O(n)时,我的程序速度总是非常慢。 First of all, I don't even know how to make it O(log n), or I've never heard about this paradigm. 首先,我甚至不知道如何制作O(log n),或者我从未听说过这种范式。 Where can I learn about this? 我在哪里可以了解到这一点?

For example, 例如,

If you had an integer array with zeroes and ones, such as [ 0, 0, 0, 1, 0, 1 ], and now you wanted to replace every 0 with 1 only if one of it's neighbors has the value of 1, what is the most efficient way to go about doing if this must occur t number of times? 如果你有一个带有0和1的整数数组,例如[0,0,0,1,0,1],现在你想要将每个0替换为1, 只要其中一个邻居的值为1,如果必须发生这种情况,这是最有效的方法吗? (The program must do this for a number of t times) (该程序必须执行此操作数t次)

EDIT: Here's my inefficient solution: 编辑:这是我的低效解决方案:

import java.util.Scanner;

public class Main {

static Scanner input = new Scanner(System.in);

public static void main(String[] args) {

    int n;
    long t;

    n = input.nextInt();
    t = input.nextLong();
    input.nextLine();

    int[] units = new int[n + 2];
    String inputted = input.nextLine();
    input.close();
    for(int i = 1; i <= n; i++) {
        units[i] = Integer.parseInt((""+inputted.charAt(i - 1)));
    }

    int[] original;

    for(int j = 0; j <= t -1; j++) {
        units[0] = units[n];
        units[n + 1] = units[1];
        original = units.clone();

        for(int i = 1; i <= n; i++) {
            if(((original[i - 1] == 0) && (original[i + 1] == 1)) || ((original[i - 1] == 1) && (original[i + 1] == 0))) {
                units[i] = 1;
            } else {
                units[i] = 0;
            }
        }

    }

    for(int i = 1; i <= n; i++) {
        System.out.print(units[i]);
    }
}

} }

This is an elementary cellular automaton. 这是一个基本的细胞自动机。 Such a dynamical system has properties that you can use for your advantages. 这种动态系统具有可用于您的优势的属性。 In your case, for example, you can set to value 1 every cell at distance at most t from any initial value 1 (cone of light property). 例如,在您的情况下,您可以将距离任何初始值1(光锥属性)的距离最多为t的每个单元格设置为值1。 Then you may do something like: 然后你可以这样做:

  • get a 1 in the original sequence, say it is located at position p. 在原始序列中得到1,说它位于p位置。
  • set to 1 every position from pt to p+t. 从pt到p + t的每个位置都设置为1。

You may then take as your advantage in the next step that you've already set position pt to p+t... This can let you compute the final step t without computing intermediary steps (good factor of acceleration isn't it?). 然后你可以在下一步中把你的位置pt设置为p + t ...这可以让你计算最后一步t而无需计算中间步骤(加速的好因素不是吗?) 。

You can also use some tricks as HashLife, see 1 . 你也可以使用一些技巧作为HashLife,见1

As I was saying in the comments, I'm fairly sure you can keep out the array and clone operations. 正如我在评论中所说,我相当确定你可以阻止阵列和clone操作。

You can modify a StringBuilder in-place, so no need to convert back and forth between int[] and String . 您可以就地修改StringBuilder ,因此无需在int[]String之间来回转换。

For example, (note: This is on the order of an O(n) operation for all T <= N ) 例如,(注意:对于所有T <= N ,这是O(n)操作的顺序)

public static void main(String[] args) {
    System.out.println(conway1d("0000001", 7, 1));
    System.out.println(conway1d("01011", 5, 3));
}

private static String conway1d(CharSequence input, int N, long T) {
    System.out.println("Generation 0: " + input);

    StringBuilder sb = new StringBuilder(input); // Will update this for all generations

    StringBuilder copy = new StringBuilder(); // store a copy to reference current generation
    for (int gen = 1; gen <= T; gen++) {
        // Copy over next generation string
        copy.setLength(0);
        copy.append(input);

        for (int i = 0; i < N; i++) {
            conwayUpdate(sb, copy, i, N);
        }

        input = sb.toString(); // next generation string
        System.out.printf("Generation %d: %s\n", gen, input);
    }

    return input.toString();
}

private static void conwayUpdate(StringBuilder nextGen, final StringBuilder currentGen, int charPos, int N) {
    int prev = (N + (charPos - 1)) % N;
    int next = (charPos + 1) % N;

    // **Exactly one** adjacent '1'
    boolean adjacent = currentGen.charAt(prev) == '1' ^ currentGen.charAt(next) == '1';
    nextGen.setCharAt(charPos, adjacent ? '1' : '0'); // set cell as alive or dead
}

For the two samples in the problem you posted in the comments, this code generates this output. 对于您在注释中发布的问题中的两个示例,此代码生成此输出。

Generation 0: 0000001
Generation 1: 1000010
1000010
Generation 0: 01011
Generation 1: 00011
Generation 2: 10111
Generation 3: 10100
10100

The BigO notation is a simplification to understand the complexity of the Algorithm. BigO表示法是一种简化算法的复杂性。 Basically, two algorithms O(n) can have very different execution times. 基本上,两个算法O(n)可以具有非常不同的执行时间。 Why? 为什么? Let's unroll your example: 让我们展开你的例子:

  • You have two nested loops. 你有两个嵌套循环。 The outer loop will run t times. 外循环将运行t次。
  • The inner loop will run n times 内循环将运行n
  • For each time the loop executes, it will take a constant k time. 每次循环执行时,它将花费恒定的k时间。

So, in essence your algorithm is O(k * t * n) . 所以,实质上你的算法是O(k * t * n) If t is in the same order of magnitude of n , then you can consider the complexity as O(k * n^2) . 如果tn的数量级相同,则可以将复杂度视为O(k * n ^ 2)

There is two approaches to optimize this algorithm: 优化此算法有两种方法:

  • Reduce the constant time k . 减少恒定时间k For example, do not clone the whole array on each loop, because it is very time consuming (clone needs to do a full array loop to clone). 例如,不要在每个循环上克隆整个数组,因为它非常耗时(克隆需要执行完整的数组循环来克隆)。
  • The second optimization in this case is to use Dynamic Programing ( https://en.wikipedia.org/wiki/Dynamic_programming ) that can cache information between two loops and optimize the execution, that can lower k or even lower the complexity from O(nˆ2) to O(n * log n). 在这种情况下的第二个优化是使用动态编程( https://en.wikipedia.org/wiki/Dynamic_programming ),它可以在两个循环之间缓存信息并优化执行,可以降低k甚至降低O的复杂度( n2)到O(n * log n)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM