O(log n)编程

时间:2016-11-06 15:07:32

标签: java algorithm performance processing-efficiency

我正在为比赛做准备,但是当我使用O(n)时,我的程序速度总是非常慢。首先,我甚至不知道如何制作O(log n),或者我从未听说过这种范式。我在哪里可以了解到这一点?

例如,

如果你有一个带有0和1的整数数组,例如[0,0,0,1,0,1],现在你只想在其中一个邻居之后用0 替换每个0 / strong>的值为1,如果必须多次出现,最有效的方法是什么? (该程序必须执行此操作数t次)

编辑: 这是我效率低下的解决方案:

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]);
    }
}

}

3 个答案:

答案 0 :(得分:2)

这是一个基本的细胞自动机。这种动态系统具有可用于您的优势的属性。例如,在您的情况下,您可以将距离任何初始值1(光锥属性)的距离最多为t的每个单元格设置为值1。然后你可以做类似的事情:

  • 在原始序列中获得1,表示它位于p。
  • 从p-t到p + t的每个位置都设置为1。

然后你可以在下一步中把你的位置pt设置为p + t ...这可以让你计算最后一步t而无需计算中间步骤(加速的好因素不是吗?)。

您也可以使用一些技巧作为HashLife,请参阅1

答案 1 :(得分:1)

正如我在评论中所说,我相当确定你可以阻止阵列和clone操作。

您可以就地修改StringBuilder,因此无需在int[]String之间来回转换。

例如,(注意:这是所有O(n)的{​​{1}}操作的顺序

T <= 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
}

答案 2 :(得分:0)

BigO表示法是一种简化算法的复杂性。基本上,两个算法O(n)可以具有非常不同的执行时间。为什么?让我们展开你的例子:

  • 您有两个嵌套循环。外循环将运行 t 次。
  • 内部循环将 n
  • 每次循环执行时,它将持续 k 时间。

所以,实质上你的算法是 O(k * t * n)。如果 t 处于 n 的相同数量级,则可以将复杂度视为 O(k * n ^ 2)。< / p>

优化此算法有两种方法:

  • 减少恒定时间 k 。例如,不要在每个循环上克隆整个数组,因为它非常耗时(克隆需要进行完整的数组循环来克隆)。
  • 在这种情况下的第二个优化是使用动态编程(https://en.wikipedia.org/wiki/Dynamic_programming),它可以在两个循环之间缓存信息并优化执行,可以降低 k 甚至降低复杂性O(n2)至O(n * log n)。