我正在创建一个解决Project Euler Problem 303的程序
我找到f(n)
的方法几乎没有蛮力:
static BigInteger findFn(int n){
Long Fn = new Long(n);
String test = Fn.toString();
Long multiplier = new Long("1");
long counter = 0;
boolean done = false;
BigInteger fn = new BigInteger("0");
while(!done){
counter = 0;
BigInteger tempOne = new BigInteger(multiplier.toString());
BigInteger tempTwo = new BigInteger(Fn.toString());
BigInteger product = tempOne.multiply(tempTwo);
test = product.toString();
for(int i = 0; i < test.toString().length(); i++){
if(Character.getNumericValue(test.toString().charAt(i)) <= 2){
counter++;
}else{
//Is it better to set done = true here as opposed to breaking?
break; //breaks if it encounters any number in the multiple >2.
}
}
if(counter == test.length()){
fn = product;
done = true;
break;
}
multiplier++;
}
return fn;
}
它适用于大多数数字,但有一些(通常是那些以9结尾的那些)它会被卡住。 我认为BigIntegers会降低它的速度,所以首先,我是否曾在任何地方使用BigInteger而没有必要? 其次,必须有一种替代方法或某种其他技巧来减少我没有想到的循环次数。 有什么想法让我朝着正确的方向努力?
谢谢!
答案 0 :(得分:2)
我想你可以通过查看测试数字中的数字来减少67%的试验,因为如果不是0,1或2那么无关紧要剩下的就是。
考虑如果数字以1结尾,那么它乘以的数字必须以0,1或2结尾,以便结果的最后一位数字<= 2.所以你测试1然后2,如果那些不起作用,那么你测试10,11,12,然后20,21,22。所以如果测试数字以1结束,你现在已经减少了70%的试验。
对于XXX2,乘数必须以0,1,5或6结束。这将删除60%。你可以继续3-9。
答案 1 :(得分:1)
尝试另一种方式呢?
我通过从最小到大来生成由0,1和2组成的数字来解决这个问题,并查看这个数字是谁的倍数。此外,我已经使用了9,99等模式。(顺便说一下,除了它们之外你不需要BigInteger。因为你会预先计算它们,摆脱BigInteger)但我发现它们的值就像你的暴力方法一样通过递增如前所述,感兴趣的数字的倍数。结果在大约6秒内弹出。如果您想查看我的解决方案here it is。
答案 2 :(得分:0)
test
是一个字符串,因此无需在其上调用toString()
。不是一个很大的改进,但它使代码更清洁。
这是一个完全避免BigInteger的算法。
For each multiplier
Set carry = 0
Set multiple = ""
Iterate through the digits of the multiplier from right to left
Set temp = digit * n + carry
if (right-most digit of temp > 2)
break and go to next multiplier
else
Set carry = temp / 10 // drop last digit and carry result
这基本上是一个很长的乘法,只要一个数字&gt;就有机会突破它。找到2。请注意,为了解决这个问题,我们实际上不需要获得F(n)
,只需要F(n)/n
这是上面的数字迭代完成的第一个乘数。然后将每个n
的最小乘数相加。
如果没有实际尝试,我很确定这只会以int
值运行。
<强>更新强>
所以我一直在玩一些代码,并让它适用于1 <= n <= 100
。看起来像n = 999
&gt;的乘数2 ^ 31,所以我们需要使用至少64位的值。不确定乘数是多少。我的代码在LinqPad上运行超过21分钟,已经过了3.2 * 10 ^ 9但还没有结果。当然,我的代码可能存在问题。
答案 3 :(得分:0)
在前进的过程中尝试保持结果。您可以使用它们来进行倍数的下限猜测。
假设某些X的可接受的LCM是Y,Y = RX。然后你知道Y对于所有{X,2X,3X,4X,......(R-1)X}也是一个下界。
答案 4 :(得分:0)
不确定以下想法是否会有所帮助:
- 允许
P(N, M)
=M
基数10
的数字为(<= 2)
,M
为N
的倍数。- 让
G(N)
=某些M
使P(N, M)
。让
F(N)
=M
使P(N, M)
和M
最小化。重要观察:
F(N) <= G(N)
。希望
G(N)
“合理”,因为它不是无偿大而且易于计算。您可以使用F(N')
N'
N
N
(可能是G(N)
的数字)来有效地计算它。了解
G(N)
可能非常有用...... 也许你可以用它来倒退。 也许你可以用它进行某种二进制搜索。如果这种技术非常有用,我想难以找到{{1}}。其余的可能是一些聪明的计算机科学技术。
答案 5 :(得分:0)
要减少迭代次数,您可以将试验产品增加1而不是将试验乘数递增1.然后检查试验产品是否可以被f()的参数整除。这样您就可以更快地获得更大的值,因为在添加1时,您可以跳过产品中的数字值4到9。
以下C代码在2.4 GHz PC上完成不到5分钟:
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stddef.h>
#include <time.h>
typedef unsigned uint;
typedef unsigned char uint8;
typedef unsigned long long uint64;
uint64 f(uint n, uint64* multiplier)
{
uint8 carry, digits[20]; // 20 digits max for 64-bit values
uint digcnt = 1, i;
uint64 result;
assert(n > 0);
#if 0
// short cut:
//
// f(9) = 12222 = 9 * 1358
// f(99) = 1122222222 = 99 * 11335578
// f(999) = 111222222222222 = 999 * 111333555778
// f(9999) = 11112222222222222222 = 9999 * 1111333355557778
if (n == 9999)
{
*multiplier = 11112222222222222222ULL / 9999;
return 11112222222222222222ULL;
}
#endif
memset(digits, 0, sizeof(digits));
for (;;)
{
carry = 1;
for (i = 0; carry; i++)
{
assert(i < sizeof(digits));
carry += digits[i];
digits[i] = carry % 3;
carry /= 3;
}
if (i >= digcnt) digcnt = i;
result = 0;
for (i = 0; i < digcnt; i++)
result = result * 10 + digits[digcnt - 1 - i];
if (result % n == 0)
{
*multiplier = result / n;
break;
}
}
return result;
}
int main(void)
{
uint i;
uint64 sum = 0, product, multiplier;
time_t t;
char* p;
for (i = 1; i <= 10000; i++)
{
product = f(i, &multiplier);
printf("%s ", ((time(&t), p = ctime(&t)), p[strlen(p) - 1] = '\0', p));
printf("f(%u) = %llu = %u * %llu\n", i, product, i, multiplier);
sum += multiplier;
}
printf("%s ", ((time(&t), p = ctime(&t)), p[strlen(p) - 1] = '\0', p));
printf("sum(f(n)/n) = %llu\n", sum);
return 0;
}
输出:
Mon Jan 9 12:18:22 2012 f(1) = 1 = 1 * 1
Mon Jan 9 12:18:22 2012 f(2) = 2 = 2 * 1
Mon Jan 9 12:18:22 2012 f(3) = 12 = 3 * 4
Mon Jan 9 12:18:22 2012 f(4) = 12 = 4 * 3
Mon Jan 9 12:18:22 2012 f(5) = 10 = 5 * 2
Mon Jan 9 12:18:22 2012 f(6) = 12 = 6 * 2
Mon Jan 9 12:18:22 2012 f(7) = 21 = 7 * 3
Mon Jan 9 12:18:22 2012 f(8) = 112 = 8 * 14
Mon Jan 9 12:18:22 2012 f(9) = 12222 = 9 * 1358
...
Mon Jan 9 12:18:39 2012 f(9998) = 111122211112 = 9998 * 11114444
Mon Jan 9 12:22:50 2012 f(9999) = 11112222222222222222 = 9999 * 1111333355557778
Mon Jan 9 12:22:50 2012 f(10000) = 10000 = 10000 * 1
Mon Jan 9 12:22:50 2012 sum(f(n)/n) = 1111981904675169
如果您将#if 0
更改为#if 1
并启用Peter Lawrey评论中提到的捷径,则会在大约1分钟内完成。
答案 6 :(得分:0)
这是我对这个有趣问题的贡献。请原谅我使用Java和BigInteger,但根据我的松散测试,它毕竟不是这样的阻塞(计算总和[1,100]花费不到一秒,总和[1,10000]大约4,5分钟在我的2.4双重核心)。在4分钟左右的4分钟以上花费在f(9999)上。非常惊讶。
import java.math.BigInteger;
public class Main {
public static void main(String args[]) {
BigInteger result = BigInteger.ZERO;
for (int i = 1; i <= 10000; ++i) {
BigInteger r = f(BigInteger.valueOf(i));
System.out.println("i=" + i + " r=" + r);
result = result.add(r.divide(BigInteger.valueOf(i)));
}
System.out.println("result=" + result);
}
// Find smallest x * value which consists only of numbers {0, 1, 2}.
private static BigInteger f(BigInteger value) {
BigInteger retVal = value;
while (!check(retVal)) {
BigInteger remainder = remainder(retVal);
BigInteger mult = remainder.subtract(retVal.remainder(remainder))
.divide(value);
retVal = retVal.add(value.multiply(mult.max(BigInteger.ONE)));
}
return retVal;
}
// Find highest remainder for given value so that value % retVal =
// XYYYYY.. Where X > 2 and 0 <= Y <= 9.
private static BigInteger remainder(BigInteger value) {
BigInteger curVal = BigInteger.TEN;
BigInteger retVal = BigInteger.TEN;
while (value.compareTo(BigInteger.TEN) >= 0) {
curVal = curVal.multiply(BigInteger.TEN);
value = value.divide(BigInteger.TEN);
if (value.remainder(BigInteger.TEN).intValue() > 2) {
retVal = curVal;
}
}
return retVal;
}
// Check if given value contains only 0, 1 and 2.
private static boolean check(BigInteger value) {
do {
if (value.remainder(BigInteger.TEN).intValue() > 2) {
return false;
}
value = value.divide(BigInteger.TEN);
} while (value.compareTo(BigInteger.ZERO) == 1);
return true;
}
}