Trustport 2020-06-06
问题描述:
欧拉诞生于1707年4月15日,对于序列(1504170715041707 * n) mod 4503599627370517,如果一个元素小于前面发现的所有Eulercoin,则其称为Eulercoin。
例如,第一个元素是1504170715041707,为第一个Eulercoin,第二个元素为3008341430083414,由于它大于1504170715041707,所以不是Eulercoin。然而,第三个元素为8912517754604,比前面的数都小,被称为Eulercoin。前两个Eulercoins之和是1513083232796311。
求所有Eulercoin之和。
解题过程:
第一步,先根据题意暴力求解
这个代码非常容易写出来。
const INC: u64 = 1504170715041707_u64;
const MOD: u64 = 4503599627370517_u64;
let mut x = 0_u64;
let mut min = INC;
for n in 1_u64.. {
x = (x + INC) % MOD;
if x <= min {
min = x;
println!("{:20} {:20}", n, x);
}
}运行这个程序,可以输出如下结果。
1504170715041707
8912517754604
2044785486369
1311409677241
578033868113
422691927098
267349986083
112008045068
68674149121
25340253174
7346610401
4046188430
745766459
428410324
111054189
15806432
15397267
14988102
14578937
14169772
13760607
13351442
12942277
12533112
12123947
11714782
11305617
10896452
10487287
10078122
9668957
9259792
8850627
8441462
8032297
7623132
7213967
6804802
6395637
5986472
5577307
5168142
4758977
4349812
3940647
3531482
3122317
2713152
2303987
1894822
1485657
1076492
667327
258162
… … … … … … … …越往后,想找到一个Eulercoin愈发困难,必须得改进算法。
第二步,找规律
在n=2527之后,后一个eulercoin与前一个eulercoin有着递推的关系,补上一些数字之后,就能发现更为明显的关系。比如,n=2021, 6569, 30824, 116727…时,得到的数虽然不是eulercoin,但数值非常大,与4503599627370517越来越接近。

根据发现的规律,需要保存好最小的数和最大的数,不用一个数一个数的计算,效率非常高,不到1秒计算完成。
let mut sum = 1504170715041707_u64 + 8912517754604_u64 + 2044785486369_u64 + 1311409677241_u64;
let mut n_max = 2021_u64;
let mut max = 4502866251561389_u64;
let mut n = 2527_u64;
let mut x = 1311409677241_u64;
let mut min = x;
while min > 0 {
let temp_n = n + n_max;
let temp_x = (x + max) % 4503599627370517_u64;
if temp_x <= min {
n = temp_n;
x = temp_x;
min = x;
sum += x;
println!("{:20} {:12} {:20}", n, x, sum);
}
if temp_x > max {
n_max = temp_n;
max = temp_x;
//println!("max: {} {} ", n_max, max);
}
}?
再优化
看了欧拉论坛中的优秀贴子,发现n的索引值并不需要记录,代码还可以更优美一些。
let inc = 1504170715041707_u64;
let modular = 4503599627370517_u64;
let mut low = inc;
let mut high = inc;
let mut sum = inc;
while low > 0 {
let next = (low + high) % modular;
if next < low {
low = next;
sum += low;
println!("{:20} {:20}", low, sum);
} else {
high = next;
}
}
println!("{}", sum);