筛选优化

时间:2012-04-06 16:25:06

标签: algorithm optimization

根据自然数序列创建序列:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

第二步中删除每第二个号码:

1 3 5 7 9 11 13 15 17 19 21 23

第3步中删除每3个号码(来自上一个序列):

1 3 7 9 13 15 19 21 

第4步中删除每第4个号码(来自上一个序列):

1 3 7 13 19

依此类推...... 现在,我们可以说,序列的第4个数字将是13。

定义和正确的解决方案是:http://oeis.org/A000960

我的任务是找到序列的第1000个成员。 我已经为此编写了一个算法,但我觉得它很慢(当我尝试使用第10,000个成员时需要大约13秒)。它的作用是:

  • 我知道,number每一步都会增加2 没有偶数。

  • counters数组中,我存储每个步骤的索引。如果数字是 xth步骤中的xth,我必须删除它,例如第3步中排名第5。和 我为下一步启动了一个计数器。

    ArrayList<Long> list = new ArrayList<Long>(10000);
    long[] counters = new long[1002];
    long number = -1;
    int active_counter = 3;
    boolean removed;
    counters[active_counter] = 1;
    int total_numbers = 1;
    
    while (total_numbers <= 1000) {
        number += 2;
        removed = false;
        for (int i = 3; i <= active_counter; i++) {
            if ((counters[i] % i) == 0) {
                removed = true;
                if (i == active_counter) {
                    active_counter++;
                    counters[active_counter] = i;
                }
                counters[i]++;
                break;
            }
            counters[i]++;
        }
        if (!removed) {
            list.add(number);
            total_numbers++;
        }
    }
    

4 个答案:

答案 0 :(得分:4)

您与OEIS的链接为我们提供了一些快速计算方法(公式等)

第二个实施:

function Flavius(n: Integer): Integer;
var
  m, i: Integer;
begin
  m := n * n;
  for i := n - 1 downto 1 do
    m := (m - 1) - (m - 1) mod i;
  Result := m;
end;

P.S。算法是线性的(O(n)),n = 10000的结果是78537769

答案 1 :(得分:2)

这个问题不是NP难......

我有直觉O(n^2),链接证明了它:

Let F(n) = number of terms <= n. Andersson, improving results of Brun,
shows that F(n) = 2 sqrt(n/Pi) + O(n^(1/6)). Hence a(n) grows like Pi n^2 / 4.

它认为O(n^2)不应该给n = 10000的15s。是的,有些东西不正确:(

修改:

我衡量了对countersn = 10000)的访问次数,以便大致了解复杂性,我有

 F = 1305646150
 F/n^2 = 13.05...

您的算法介于O(n^2)O(n^2*(logn))之间,因此您正在做正确的事情.... :)

答案 2 :(得分:1)

哇,这是一个非常有趣的问题。
非常感谢你。

我刚刚失去了一小时的生命。我认为问题将变成NP难。而且我无法生成一个方程来计算j th 步骤中的i th 项。

除非有一些聪明的数学技巧可以在一步中生成最终解决方案,否则您的“暴力”解决方案似乎很好。但我认为没有。

从编程的角度来看,您可以尝试将初始数组作为链接列表,然后取消链接要删除的术语。这样可以节省一些时间,因为你不会每一步都重建你的列表。

答案 3 :(得分:1)

一种方法可能是保留您用来筛选的数字数组,而不是筛选的数字。基本上,如果要查找序列中的第N个值,则创建一个N个计数器的数组,然后遍历自然数。对于每个数字,循环遍历计数器,递增它们直到达到“最大”值,此时将该计数器设置为零并停止递增剩余的计数器。 (这表示在该计数器的步骤中删除当前数字。)如果您通过所有计数器而不删除当前数字,那么这是剩下的数字之一。

一些样本(Java)代码似乎与OEIS给出的序列匹配:

public class Test {
  public static void main(String[] args) {
    int N=10000;
    int n=0;
    long c=0;

    int[] counters = new int[N];

    outer: while(n<N) {
      c++;
      for(int i=0;i<N;i++){
        counters[i]++;
        if(counters[i]==i+2){
          counters[i]=0;
          continue outer;
        }
      }

      // c is the n'th leftover
      System.out.println(n + " " + c);
      n++;
    }
  }
}

我相信这是在O(N ^ 3)中运行。