我正在解决一个问题,即生成第一个n< 1000000幸运数字,但问题是它必须在2秒内生成它们,我尝试了不同的方法,但我总是有时间限制,我也在考虑使用布尔值更改此算法,但没有结果。
在这里你可以找到幸运数字的筛子作为参考http://en.wikipedia.org/wiki/Lucky_number
幸运数字序列如下 1,3,7,9,13,15,21,25,31,33,37,43,49,51,63,67,69,73,75,79,87,93,99 ......
这是我使用ArrayList创建的低效的代码,如果你有任何我可以用来解决它的提示,我会很感激。
public class Luckyumbers {
public static void main(String[] args) {
ArrayList<Integer> numbers = new ArrayList<Integer>();
Integer max = 200000;
int b;
int c = 0;
int p;
for (int i = 1; i <= max; i += 2) {
numbers.add(i);
}
// System.out.println(numbers);
Integer bbb = 3;
Integer j = numbers.size();
int a = 3;
while (bbb < j) {
b = numbers.size();
p = 1;
for (int i = bbb; i < b; i += bbb) {
numbers.remove(i-p);
p = p + 1;
}
b = numbers.size() - 1;
c = numbers.get(b);
j = j - numbers.size() / bbb;
bbb = numbers.get(a-1);
a = a + 1;
// System.out.println(numbers);
}
for (int k = 0; k < numbers.size(); k++) {
int z = numbers.get(k);
System.out.print(z + " ");
}
}
}
改进版本我更改了代码,其中包含了每个人都给我的建议,我将算法的时间减少到了13秒,但仍然减少了
public static void main(String[] args) {
int max= 1000000;
boolean[] numbers = new boolean[max];
for (int i = 2; i < numbers.length; i+=2)
numbers[i]=true;
int k=2,j=0,l=0,ant=0;
int p=0;
for (int i = 2; (k+k) < numbers.length; i++) {
k=which(numbers,i);
l=0;
p=0;
int sw=0;
boolean untrue=false;
for (j = l; l < numbers.length; j++) {
if((p==k)&&sw==1){
numbers[l]=true;
untrue=true;
p=0;
}
l=Next(numbers,l);
//if (sw ==1)
p++;
sw=1;
}
if (!untrue)
break;
}
System.out.println(counter(numbers));
}
static int which(boolean[] numbers,int i){
int k=0;
int l;
for (l = 0; l < i; l++) {
k=Next(numbers,k);
}
return k;
}
static int Next(boolean[] numbers,int i){
for (int l = i+1; l < numbers.length; l++) {
if (numbers[l]==false)
return l;
}
return numbers.length+1;
}
static int counter(boolean[] numbers){
int c=0;
for (int j = 1; j < numbers.length; j++)
if(numbers[j]==false)
c++;
return c;
}
}
答案 0 :(得分:5)
使用标志数组(并且从筛子中消除时将每个元素设置为特殊值)会(可能)快得多。这样您就不需要创建N个Integer对象,将它们添加到集合中,然后再次删除它们。
要注意的一点是确定下一次迭代的“筛子”倍数......
其他几个答案讨论了从ArrayList
中删除元素时效率低下的问题。
另请注意,首先创建对象需要时间。创建一个int[]
个10M元素并为每个元素写一个值在我的系统上花费50ms,但对Integer
个对象进行相同操作需要1100ms,这超过了目标时间的一半设置!
创建和填充10M元素ArrayList<Integer>
需要1500毫秒,即使您预先调整它的大小,LinkedList<Integer>
需要3200毫秒,所以在您开始筛选之前就没时间了。< / p>
更新:尝试过此操作(没有 btilly 建议的特殊外壳),确实要快得多,在原始设备上扫描的输入数量为8.6秒,原始设备为32.5秒和35M的10M输入数字与原始的137s输入数字。
我也尝试使用位数组而不是int
数组,这显然可以节省大量内存但速度只有一半。
另一个想法 - 关于质数筛子的问题有很多问题,也可能讨论类似的性能技术?
答案 1 :(得分:2)
使用ArrayList执行此操作效率很低,因为每次从列表中删除元素时,列表中的所有其他数字都必须复制到先前的位置。使用LinkedList可能会有所帮助,但如果您只使用一个数组并在必须删除的索引处放置一些标记值(例如-1),它会更快。如果您尝试使用LinkedList,请确保永远不要调用get()
方法,但要使用迭代器,并使用Iterator的remove方法删除元素。
另外,使用List强制不断地从int转换为Integer,反之亦然。
您还可以通过将ArrayList的初始大小设置为正确的值来获得一些时间。否则,它会强制它在每次必须扩展时制作内部数组的副本。
答案 2 :(得分:2)
每次调用remove
时,都在调用O(n)操作,因为删除后的所有元素都需要逐个移向数组的开头。尝试使用LinkedList
来提高性能。但是,get
操作将为O(n)。尝试使用迭代器来解决这个问题。 (您必须手动执行iterator.next()
七次才能从当前元素中获取第七个元素,但这是值得的。)
你的表现现在是O(n ^ 3),LinkedList
它应该是O(n ^ 2)。理由:
使用LinkedList
,第一个操作是O(1)。
答案 3 :(得分:0)
顺序很好,这些幸运数字(+1)。由于我想知道布尔数组是否是最优的,我使用了一个数组来保存尚未过滤掉的下一个元素的索引,请参阅下面的实现。有了这个,我不必为每个数字2,3,7,9迭代整个数组,但是可以跳过已经过滤掉的数组的片段。
public class LuckyNumbers {
List<Integer> result;
int sieveSize = -1;
public LuckyNumbers(final int length) {
sieveSize = length;
result = new ArrayList<Integer>();
final int[] sieve = new int[sieveSize];
int oldIndex = 1;
//initialize: each array-element points to the next index; index corresponds to number (ignore sieve[0] for simplicity)
for (int index = 2; index <= sieveSize; index++) {
sieve[oldIndex] = index;
oldIndex = index;
}
}
private void computeWithSieve() {
int oldIndex = 1;
// filters are 2,3,7,9,...
for (int filter = 2; filter < sieveSize; filter = sieve[filter]) {
// System.out.println("Filter " + filter);
int moduloCounter = 1;
oldIndex = -1;
// if outdated filter-indexes are not set to -1: use filter == 2 ? 3 : sieve[filter] instead
for (int index = 1; index < sieveSize; index = sieve[index]) {
assert moduloCounter <= filter;
if (moduloCounter == filter) {
moduloCounter = 1;
assert oldIndex > 0;
sieve[oldIndex] = sieve[index];
//unnecessary: sieve[index] = -1;
//jump back to oldIndex, such that nextElement() in the loop finds the correct successor:
index = oldIndex;
} else {
oldIndex = index;
moduloCounter++;
}
}
}
// for (int index = 1; index < sieveSize; index = sieve[index]) {
// result.add(index);
// }
}
public String toString() {
return result.toString();
}
public static void main(String[] args) {
int size = (args.length == 0 || args[0] == null ? 100000 : new Integer(args[0]));
LuckyNumbers ln = new LuckyNumbers(size);
long start = System.currentTimeMillis();
ln.computeWithSieve();
System.out.println(System.currentTimeMillis() - start + "milliseconds required for " + ln.result.size());
// System.out.println("Sieve: "+ln);
}
}
不幸的是,我只能在2秒内计算高达100.000的幸运数字。但话说回来,只需在我的慢速计算机上初始化1000000个元素阵列需要大约1秒钟。所以我认为2秒的时间框是不可行的。