在维基百科页面上读取重复小数的信息后,我找到了一种方法来查找小数点重复部分的位数。
例如,
1/3 = 0.333333333333333333333333333333 ...所以结果是1位数。
1/7 = 0.142857142857142857142857142857 ...所以结果是6位数。
然而,我的方法(在Java中)不适用于1/6,它应该产生1,因为:
1/6 = 0.1666 ...所以尽管十进制的非重复部分,结果是1位数。
我找到了一个有效的解决方案(归功于Nayuki Minase)。
private static int getCycleLength(int n)
{
Map<Integer,Integer> stateToIter = new HashMap<Integer,Integer>();
int state = 1;
int iter = 0;
while (!stateToIter.containsKey(state))
{
stateToIter.put(state, iter);
state = state * 10 % n;
iter++;
}
System.out.println(iter + " - " + stateToIter.get(state));
return iter - stateToIter.get(state);
}
有人可以向我解释这个算法是如何工作的吗?谢谢。
答案 0 :(得分:1)
所以在这个算法中,这一行是关键。
while(!stateToIter.containsKey(state))
当程序发现重复状态时会破坏程序。现在找到重复状态意味着我们正在检测重复循环。让我们解决问题,说我们必须找出6。 我们做1/6的方式是
Problem :6 | 1 | Result = ?
Step 1:
Add 0. in the result and multiply 1 with 10
6 | 10 | 0.
Iteration
Step 2:
Do the division
6 | 10 | 0.1
6
-----
4 [Mod]
Iteration = 0
Step 3:
Multiply mod with 10 and carry on
6 | 10 | 0.16
6
-----
40
36
-----
04 [Mod]
Iteration = 1
Now we find a repeating mod so now matter how far we go we always get 4 as mod and our result will be 0.166666.. and so on so our repeating cycle will be 1 which is our iteration.
答案 1 :(得分:1)
Nayuki在这里。代码来自Project Euler p026.java。让我解释一下发生了什么。
主要思想是我们模拟长除法并检测余数何时开始重复。让我们用计算1/7的例子来说明。
0.142857...
-------------
7 | 1.000000000
7
---
30
28
--
20
14
--
60
56
--
40
35
--
50
49
--
10
...
要执行长除法,我们执行以下步骤:
设置divisor = 7.设置dividend = 1.(我们计算1/7。)
----
7 | 1
除数进入股息的次数是多少?让它为 k 。将此数字附加到商。
0
---
7 | 1
从被除数中减去 k ×除数。这是余下的。
0
---
7 | 1
-0
--
1
在右侧移动一个新数字。在我们的例子中,它是零的无限小数。这相当于将股息乘以10。
0
---
7 | 1.0
-0
--
10
转到第2步并无限重复。
0.1
-----
7 | 1.0
-0
--
10
-7
--
3
...
我们在长期划分的每次迭代中更新股息。如果被除数具有先前所做的值,则它将生成相同的十进制数字。
现在,在代码中:
// Step 1
int divisor = 7;
int dividend = 1;
while (true) {
// Step 2
int k = dividend / divisor; // Floor
// Step 3
dividend -= k * divisor;
// Step 4
dividend *= 10;
}
通过一些数学运算,步骤2和3可以合并为dividend %= divisor;
。此外,这可以与步骤4结合以获得dividend = dividend % divisor * 10;
。
地图记录了第一次看到每个红利状态。在我们的例子中:
迭代6的状态与迭代0的状态相同。此外,这是最短的周期。因此,循环长度为6-0 = 6。
答案 2 :(得分:0)
当修改为十进制时,你逐渐将你的值乘以10,直到你有0(不再写)或放弃(你已达到你的精度极限)
如果您再次使用相同的值,则会从该点重复相同的数字。当您获得重复值并计算自您第一次看到它之后的时间(重复周期的长度)时,该方法的作用是找到
BTW这个问题的另一个解决方案是避免使用Map。任何重复序列必须是1/9或1/99或1/999或1/9999等的倍数。这可以找出除数需要多少个9作为因子。这是它重复的重点。
public static void main(String... args) throws IOException {
for (int i = 3; i < 100; i++) {
System.out.println("i: " + i + " f: " + 1.0 / i + " repeat: " + getRepeatingCount(i));
}
}
public static final BigInteger TEN_TO_19 = BigInteger.TEN.pow(19);
public static int getRepeatingCount(int divisor) {
if (divisor <= 0) throw new IllegalArgumentException();
while (divisor % 2 == 0) divisor /= 2;
while (divisor % 5 == 0) divisor /= 5;
int count = 1;
if (divisor == 1) return 0;
for (long l = 10; l > 0; l *= 10) {
long nines = l - 1;
if (nines % divisor == 0)
return count;
count++;
}
for(BigInteger bi = TEN_TO_19; ; bi = bi.multiply(BigInteger.TEN)) {
BigInteger nines = bi.subtract(BigInteger.ONE);
if (nines.mod(BigInteger.valueOf(divisor)).equals(BigInteger.ZERO))
return count;
count++;
}
}