Java 循环i++;vs i+=2.
我一直在尝试一种高效的算法来寻找素数,作为我尝试的一部分,我一直在使用下面的代码。我有一个想法,通过改变I+=2的激励来加速循环,但实际上,这个改变似乎使我的程序的运行时间增加了2秒。有谁能解释为什么会发生这种情况,因为循环似乎需要完成一半的工作才能完成Java 循环i++;vs i+=2.,java,Java,我一直在尝试一种高效的算法来寻找素数,作为我尝试的一部分,我一直在使用下面的代码。我有一个想法,通过改变I+=2的激励来加速循环,但实际上,这个改变似乎使我的程序的运行时间增加了2秒。有谁能解释为什么会发生这种情况,因为循环似乎需要完成一半的工作才能完成 for(int i = answers.get(answers.size()-1)+2;i<n;i++) { int bit = i%64; int currentInt = i/64;
for(int i = answers.get(answers.size()-1)+2;i<n;i++) {
int bit = i%64;
int currentInt = i/64;
int isPrime = (primes[currentInt] >> bit) & 1;
if(isPrime == 1) {answers.add(i);}
}
for(inti=answers.get(answers.size()-1)+2;i>bit)&1;
如果(isPrime==1){answers.add(i);}
}
完整代码如下:
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
public class Primes15 {
static Long start;
public static IntStream stream() {
int numbers = 15350808;
int n = 982451712;
List<Integer> answers = new ArrayList<>();
long[] inverseShifts = new long[64];
long temp = 1;
for(int i = 0; i < inverseShifts.length; i++) {
inverseShifts[i] = temp ^ -1;
temp = temp << 1;
}
long[] primes = new long[numbers+1];
primes[0] = -6148914691370734930L;
for(int i = 1;i<primes.length; i++) {
primes[i] = -6148914691370734934L;
}
System.out.println("Setup taken " + (System.currentTimeMillis() - start) + " millis");
start = System.currentTimeMillis();
for(int p =3; p*p <=n; p+=2) {
int bit = p%64;
int currentInt = p/64;
long isPrime = (primes[currentInt] >> bit) & 1;
if(isPrime == 1) {
answers.add(p);
int cPrimeSquared = p*p;
int change = (p==2)? p : p+p;
for(int i = cPrimeSquared; i <= n; i += change) {
int innerBit = i % 64;
int innerInt = i /64;
isPrime = (primes[innerInt] >> innerBit) & 1;
if(isPrime == 1) {
primes[innerInt] = primes[innerInt] & inverseShifts[innerBit];
}
}
}
System.out.println("finder took " + (System.currentTimeMillis() - start) + " ms");
start = System.currentTimeMillis();
for(int i = answers.get(answers.size()-1)+2; i<n; i++) {
int bit = i%64;
int currentInt = i/64;
long isPrime = (primes[currentInt] >> bit) & 1;
if(isPrime == 1) {answers.add(i);}
}
System.out.println("search took " + (System.currentTimeMillis() - start) + " ms");
start = System.currentTimeMillis();
return answers.stream().mapToInt(i->i);
}
public static void main(String[] args) {
start = System.currentTimeMillis();
stream();
Long finish = System.currentTimeMillis();
System.out.println("Time taken " + (finish - start) + " millis");
}
}
import java.util.ArrayList;
导入java.util.List;
导入java.util.stream.IntStream;
公共类素数15{
静态长启动;
公共静态IntStream(){
整数=15350808;
int n=982451712;
列表答案=新建ArrayList();
long[]inverseShifts=新长[64];
长期温度=1;
对于(int i=0;ibit)&1;
如果(isPrime==1){answers.add(i);}
}
System.out.println(“搜索时间”+(System.currentTimeMillis()-start)+“ms”);
start=System.currentTimeMillis();
返回answers.stream().mapToInt(i->i);
}
公共静态void main(字符串[]args){
start=System.currentTimeMillis();
流();
长时间完成=System.currentTimeMillis();
System.out.println(“所用时间”+(完成-开始)+“毫秒”);
}
}
我用JMH做了一些测试-因为整个代码都丢失了,而且仍然很难阅读,所以我使用了一个简化的、朴素的版本(不适用于查找素数,但具有类似的计算IMHO):
也就是说,按每个操作的纳秒排序:
预期的种类:增加2会快2倍;令人惊讶的是,i+=1
比i++
慢了一点-我假设编译器为这两种代码创建相同的操作码
第一次联系JMH,不确定我是否100%
正确,但必须测试它[:-)你能把你的
答案
、素数
和移位
声明包括在内吗?我实际上已经改变了,那一行将被更新。答案被声明为空,但它已经有许多素数被添加到31337。素数是一个长数组,此时其中有15350809个数字。移位是一个由64个长数字组成的数组,第一个是63 0,后面是1,每个数字都按位置移位(1、10、100等)。给定的代码甚至不应该编译(假设primes
确实是long[]
)-缺少int的转换;shift
根本不被使用(发布/编辑的代码);我们不知道花了多少时间;请在问题中发布并添加其他信息,而不是(仅)作为评论。我认为您应该对其进行分析,甚至不清楚问题是否存在于循环中。如果您希望我们阅读您的代码,请重新格式化以删除多余的空白。
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class Increments {
private long[] primes;
@Setup
public void setup() {
primes = new long[] {3, 5, 7, 11, 13, 17, 19, 23};
}
@Benchmark
@Fork(1)
public List<Integer> inc() {
List<Integer> answers = new ArrayList<>();
for (int i = 3; i < 100; i++) {
int bit = i % 32;
int cur = i / 32;
long test = (primes[cur] >> bit) & 1;
if (test == 1) {
answers.add(i);
}
}
return answers;
}
@Benchmark
@Fork(1)
public List<Integer> addOne() {
List<Integer> answers = new ArrayList<>();
for (int i = 3; i < 100; i+=1) {
int bit = i % 32;
int cur = i / 32;
long test = (primes[cur] >> bit) & 1;
if (test == 1) {
answers.add(i);
}
}
return answers;
}
@Benchmark
@Fork(1)
public List<Integer> addTwo() {
List<Integer> answers = new ArrayList<>();
for (int i = 3; i < 100; i+=2) {
int bit = i % 32;
int cur = i / 32;
long test = (primes[cur] >> bit) & 1;
if (test == 1) {
answers.add(i);
}
}
return answers;
}
}
Benchmark Mode Cnt Score Error Units
Increments.addOne avgt 5 304,670 ± 73,226 ns/op
Increments.addTwo avgt 5 131,429 ± 13,616 ns/op
Increments.inc avgt 5 249,329 ± 14,866 ns/op
i+=2 131ns
i++ 249ns
i+=1 304ns