简体   繁体   English

筛分优化

[英]Sieve optimization

A sequence is created from sequence of natural numbers:一个序列是由自然数序列创建的:

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

removing every 2nd number in the 2nd step :第二步中删除每个第二个数字:

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

removing every 3rd number in the 3rd step (from previous sequence):在第3 步中删除每个第 3 个数字(从前面的序列):

1 3 7 9 13 15 19 21 

removing every 4th number in the 4th step (from previous sequence):删除第4 步中的每 4 个数字(从之前的序列):

1 3 7 13 19

and so forth... Now, we're able to say, that the 4th number of the sequence will be 13.依此类推...现在,我们可以说序列的第 4 个数字将是 13。

Definition and the right solution for this is here: http://oeis.org/A000960定义和正确的解决方案在这里: http://oeis.org/A000960

My task is to find a 1000th member of the sequence.我的任务是找到序列的第 1000 个成员。 I have written an algorithm for this, but I think it's quite slow (when I try it with 10.000th member it takes about 13 seconds).我为此编写了一个算法,但我认为它很慢(当我对第 10.000 个成员进行尝试时,大约需要 13 秒)。 What it does is:它的作用是:

  • I have number which increases by 2 in every step, since we know that there ain't no even numbers.我有每一步都增加 2 的number ,因为我们知道没有偶数。

  • In counters array I store indexes for each step.counters数组中,我为每个步骤存储索引。 If the number is xth in xth step, i have to remove it, eg number 5 in 3rd step.如果第 x 步中的数字是 xth,我必须将其删除,例如第 3 步中的数字 5。 And I initiate a counter for the next step.然后我为下一步启动一个计数器。

     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++; } }

Your link to OEIS gives us some methods for fast calculation (FORMULA etc)您与 OEIS 的链接为我们提供了一些快速计算的方法(公式等)

Implementation of the second one:第二个的实现:

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;

PS Algorithm is linear (O(n)), and result for n=10000 is 78537769 PS 算法是线性的 (O(n)),n=10000 的结果是 78537769

No this problem is not NP hard...不,这个问题不是 NP 难...

I have the intuition it is O(n^2) , and the link proove it:我的直觉是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.

It think O(n^2) should not be give 15s for n = 10000. Yes there is something not correct:(它认为O(n^2)不应该给 n = 10000 15s。是的,有些东西不正确:(

Edit:编辑:

I measured the number of access to counters (for n = 10000 )to get a rough idea of the complexity and I have我测量了访问counters的次数(对于n = 10000 )以大致了解复杂性并且我有

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

Your algorithm is between O(n^2) and O(n^2*(logn)) so you are doing things right.... :)您的算法介于O(n^2)O(n^2*(logn))之间,因此您做对了……:)

Wow, that is a really interesting problem.哇,这真是一个有趣的问题。
Thank you so much for that.对此感激不尽。

I just lost an hour of my life to this.我刚刚为此失去了一个小时的生命。 I think the problem will turn out to be NP-hard.我认为这个问题会变成 NP-hard。 And I am at a loss to generate an equation to calculate the i th term in the j th step.我不知道如何生成一个方程来计算第j步中的第i项。

Your "brute force" solution seems fine unless there is some clever math trick to generate the final solution in one step.您的“蛮力”解决方案似乎不错,除非有一些聪明的数学技巧可以一步生成最终解决方案。 But I do not think there is.但我认为没有。

From a programming standpoint, you could try making your initial array a linked list and just un-linking the terms you want to drop.从编程的角度来看,您可以尝试将初始数组设为链表,然后取消链接要删除的项。 That would save you some time, since you wouldn't be rebuilding your list every step.这会节省你一些时间,因为你不会每一步都重建你的列表。

One approach could be to keep an array of the numbers you are using to sieve, rather than the numbers being sieved.一种方法可能是保留您用来筛选的数字数组,而不是保留要筛选的数字。 Basically, if you are looking for the Nth value in the sequence, you create an array of N counters and then iterate through the natural numbers.基本上,如果您正在寻找序列中的第 N 个值,您可以创建一个包含 N 个计数器的数组,然后遍历自然数。 For each number, you loop through your counters, incrementing them until one gets to its "maximum" value, at which point you set that counter to zero and stop incrementing the remaining counters.对于每个数字,您循环遍历您的计数器,递增它们直到一个达到其“最大值”值,此时您将该计数器设置为零并停止递增其余计数器。 (This represents removing the current number at that counter's step.) If you get through all of the counters without removing the current number, then this is one of the numbers that is left over. (这表示在该计数器的步骤中删除当前数字。)如果您通过所有计数器而不删除当前数字,那么这是剩下的数字之一。

Some sample (Java) code that seems to match the sequence given by OEIS:一些示例 (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++;
    }
  }
}

I believe this runs in O(N^3).我相信这在 O(N^3) 中运行。

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

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