import java.io.*;
import java.util.ArrayList;
public class Ristsumma {
static long numberFromFile;
static long sum1, sum2;
static long number, number2;
static long variable, variable2;
static long counter;
public static void main(String args[]) throws IOException{
try{
BufferedReader br = new BufferedReader(new FileReader("ristsis.txt"));
numberFromFile = Long.parseLong(br.readLine());
br.close();
}catch(Exception e){
e.printStackTrace();
}
variable=numberFromFile;
ArrayList<Long> numbers = new ArrayList<Long>();
while (variable > 0){
number = variable %10;
variable/=10;
numbers.add(number);
}
for (int i=0; i< numbers.size(); i++) {
sum1 += numbers.get(i);
}
ArrayList<Long> numbers2 = new ArrayList<Long>();
for(long s=1; s<numberFromFile; s++){
variable2=s;
number2=0;
sum2=0;
while (variable2 > 0){
number2 = variable2 %10;
variable2/=10;
numbers2.add(number2);
}
for (int i=0; i< numbers2.size(); i++) {
sum2 += numbers2.get(i);
}
if(sum1==sum2){
counter+=1;
}
numbers2.clear();
}
PrintWriter pw = new PrintWriter("ristval.txt", "UTF-8");
pw.println(counter);
pw.close();
}
}
所以我有这个代码。它从文件中取一个数字,将所有数字与该数字分开添加并将它们加在一起(例如,数字为123,然后它给出1 + 2 + 3 = 6)。在下半部分,它会查找文件中从1到该数字的所有数字,并计算有多少不同的数字给出相同的答案。如果数字是123,则总和为6,代码写入的答案为9(因为6,15,24,33,42,51,60,105,114也给出相同的答案)。代码有效,但我的问题是,当文件中的数字是例如2 222 222 222时,则需要将近半小时才能得到答案。如何让这个运行更快?
答案 0 :(得分:3)
您不必要地创建列表
ArrayList<Long> numbers = new ArrayList<Long>();
while (variable > 0){
number = variable %10;
variable/=10;
numbers.add(number);
}
for (int i=0; i< numbers.size(); i++) {
sum1 += numbers.get(i);
}
在这里你创建一个arraylist,只是暂时持有Longs,你可以删除整个列表
while (variable > 0){
number = variable %10;
variable/=10;
sum1 += number
}
其他arraylist numbers2
也是如此我们已经淘汰了arraylists,但如果我们没有,我们可以通过预设阵列来提高速度
ArrayList<Long> numbers = new ArrayList<Long>(someGuessAsToSize);
你的猜测是正确的并不是必须的,arraylist仍会自动调整大小,但如果猜测大致正确,你将加速代码,因为arraylist不必定期调整大小。
你持有很多(应该是)方法变量作为字段
static long numberFromFile;
static long sum1, sum2;
static long number, number2;
static long variable, variable2;
static long counter;
这不太可能影响性能,但这是一件不寻常的事情,并且使代码的可读性降低,并且潜在的“隐藏效果”
答案 1 :(得分:2)
你的问题很有趣 - 它让我想知道它会用线程运行多快。
这是一个线程实现,它分割了跨线程计算第二个问题的任务。我的笔记本电脑只有两个内核,所以我将线程设置为4。
public static void main(String[] args) throws Exception {
final long in = 222222222;
final long target = calcSum(in);
final ExecutorService executorService = Executors.newFixedThreadPool(4);
final Collection<Future<Integer>> futures = Lists.newLinkedList();
final int chunk = 100;
for (long i = in; i > 0; i -= chunk) {
futures.add(executorService.submit(new Counter(i > chunk ? i - chunk : 0, i, target)));
}
long res = 0;
for (final Future<Integer> f : futures) {
res += f.get();
}
System.out.println(res);
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.DAYS);
}
public static final class Counter implements Callable<Integer> {
private final long start;
private final long end;
private final long target;
public Counter(long start, long end, long target) {
this.start = start;
this.end = end;
this.target = target;
}
@Override
public Integer call() throws Exception {
int count = 0;
for (long i = start; i < end; ++i) {
if (calcSum(i) == target) {
++count;
}
}
return count;
}
}
public static long calcSum(long num) {
long sum = 0;
while (num > 0) {
sum += num % 10;
num /= 10;
}
return sum;
}
它会在几秒钟内以222 222 222
作为输入计算解决方案。
我优化了sum
的计算,以删除您正在使用的所有List
。
修改强>
我使用Stopwatch
添加了一些计时代码,并使用222222222 * 100
作为输入数字,尝试使用和不使用@ Ingo进行优化。
如果没有优化,代码需要35秒。将calc方法更改为:
public static long calcSum(long num, final long limit) {
long sum = 0;
while (num > 0) {
sum += num % 10;
if (limit > 0 && sum > limit) {
break;
}
num /= 10;
}
return sum;
}
添加优化后,代码需要28秒。
请注意,这是一个非常非科学的基准,因为我没有对JIT进行温暖或进行多次试验(部分原因是因为我很懒,部分是因为我很忙)。
修改强>
摆弄块大小也会产生相当不同的结果。大约1000次下降到大约17秒。
修改强>
如果你想真正喜欢,你可以使用ForkJoinPool
:
public static void main(String[] args) throws Exception {
final long in = 222222222;
final long target = calcSum(in);
final ForkJoinPool forkJoinPool = new ForkJoinPool();
final ForkJoinTask<Integer> result = forkJoinPool.submit(new Counter(0, in, target));
System.out.println(result.get());
forkJoinPool.shutdown();
forkJoinPool.awaitTermination(1, TimeUnit.DAYS);
}
public static final class Counter extends RecursiveTask<Integer> {
private static final long THRESHOLD = 1000;
private final long start;
private final long end;
private final long target;
public Counter(long start, long end, long target) {
this.start = start;
this.end = end;
this.target = target;
}
@Override
protected Integer compute() {
if (end - start < 1000) {
return computeDirectly();
}
long mid = start + (end - start) / 2;
final Counter low = new Counter(start, mid, target);
final Counter high = new Counter(mid, end, target);
low.fork();
final int highResult = high.compute();
final int lowResult = low.join();
return highResult + lowResult;
}
private Integer computeDirectly() {
int count = 0;
for (long i = start; i < end; ++i) {
if (calcSum(i) == target) {
++count;
}
}
return count;
}
}
public static long calcSum(long num) {
long sum = 0;
while (num > 0) {
sum += num % 10;
num /= 10;
}
return sum;
}
在另一台(速度快得多)的计算机上运行时间不到一秒,而原始方法则为2.8秒。
答案 2 :(得分:1)
注意您根本不需要存储个别数字。
相反,你感兴趣的只是数字的实际总和。
考虑到这一点,像
这样的方法static int diagsum(long number) { ... }
会很棒。如果它很简单,JIT可以内联它,或者至少比你的意大利面条代码更好地优化。
然后,您可以从另一种方法中受益,该方法可以在某个限度内停止计算数字和。例如,当你有
时22222222
总和为20,这意味着您无需计算任何大于20的其他总和。例如:
45678993
相反,你可以在你拥有最后3位数(你通过你的diision方法得到的数字)之后停止,因为9 + 9 + 3是21并且这已经大于20。
=============================================== ====================
另一个优化:
如果你有一些号码:
123116
很明显,这6个数字的所有唯一排列具有相同的数字总和,因此
321611, 231611, ... are solutions
然后,对于任何一对单独的数字ab,转换后的数字将包含(a + 1)(b-1)和(a-1)(b + 1)在同一个地方,只要a + 1 ,...仍然在0..9范围内。递归应用以获得更多的数字。
然后您可以转到数字较少的数字。显然,要具有相同的数字总和,如果可能,您必须组合原始数字的2位数,例如
5412 => 912, 642, 741, 552, 561, 543
等。 如上所述递归地应用相同的算法,直到不可能进行转换和组合。
======
必须要说的是,上述想法需要大量内存,因为必须保持类似Set的数据结构来处理重复。但是,对于987_654_321,我们已经获得了39_541_589个结果,并且可能更多的数字更多。因此,以组合方式实际做到这一点的努力是值得的,这是值得怀疑的。
答案 3 :(得分:1)
您大部分时间都在检查未通过测试的号码。然而,正如Ingo所观察到的,如果你有一个数字ab,那么(a-1)(b + 1)与ab的总和相同。您可以生成它们,而不是检查所有数字:
让我们说我们的数字是2 222,总和是8。 方法#1:自下而上
我们现在生成从最小的开始的数字(我们用零填充以便于阅读):0008。下一个是0017,下一个是0026,0035,0044,0053,0062,0071,0080,0107等等上。有问题的部分是找到第一个有此总和的数字。
方法#2:自上而下
我们从2222开始,下一个较低的数字是2213,然后是2204,2150,2141,依此类推。在这里,您没有需要找到最小数字的问题。
我现在没有时间编写代码,但是应该有一种算法来实现这两种方法,而不涉及尝试所有数字。
对于数字abc,(a)(b-1)(c + 1)是下一个较低的数字,而(a)(b + 1)(c-1)是下一个较高的数字。唯一有趣/困难的事情是当你需要溢出因为b == 9或c == 9,或b == 0,c == 0。如果b == 9则下一个更大的数字是(a + 1)(9)(c-1)如果c> 0,并且(a)(8)(0)如果c == 0。现在去制作你的算法,这些例子应该足够了。