我有两个整数x和y。
规则:
问题是,这两个整数是否为无限循环?
这是我的代码:
private static boolean IsPairLoop(int first, int second)
{
boolean loop = false;
HashMap<Integer, Integer> round_record = new HashMap<>();
int[] first_round = new int[]{first, second};
while ((first_round[0] != -1 && first_round[1] != -1))
{
if (round_record.containsKey(first_round[0]))
{
loop = true;
break;
}
round_record.put(first_round[0], first_round[1]);
PlayRound(first_round);
}
return loop;
}
private static void PlayRound(int[] round)
{
if (round[0] > round[1])
{
round[0] -= round[1];
round[1] += round[1];
}
else if (round[0] < round[1])
{
round[1] -= round[0];
round[0] += round[0];
}
else
{
round[0] = -1;
round[1] = -1;
}
}
这对于小整数很好用。但是,当整数差很大时,这会非常缓慢。 x和y的整数范围均为1到2 ^ 30。即使整数差异很大,我该怎么做才能使速度更快?
答案 0 :(得分:3)
在此问题中,总和x + y
是不变的,如果它是奇数,则x == y
是不可能的。
然后,如果x
和y
具有相同的奇偶校验,则在一次迭代之后,它们都变为偶数并保持不变,如果将两者都除以2
,问题就不会改变。
因此是“即时”解决方案:
while (x + y) & 1 == 0:
if x == y:
print "Not infinite"
break
if x > y:
x= (x - y) / 2
else:
y= (y - x) / 2
else:
print "Infinite"
由于其中一个自变量在每次迭代中都会损失至少一位,因此对于32位整数,迭代次数绝不会超过64次(实际上,更少的情况是,大多数情况下为0!)。
可能存在变体,并且对大数有意义:
如果x == y
,则为有限结论。
如果x
和y
的尾随零个数不同,则得出无限大结论。
否则,丢弃尾随的零并根据x > y
或y > x
进行归约并循环。
答案 1 :(得分:1)
使用Floyd的循环检测算法,而不是使用哈希图。这样不仅可以避免占用大量内存,还可以避免在int
和Integer
之间进行昂贵的装箱和拆箱。
第二个优化是通过更改变量来重写递归关系:
s = x+y
t = x-y
则递归关系变为:
if t=0, stop
if t>0, s'=s, t'=2t-s
if t<0, s'=s, t'=2t+s
请注意,在此公式中,仅t
变量会更改。
代码(未经测试)将如下所示:
private static int step(int s, int t) {
if (t>0) return 2*t - s;
if (t<0) return 2*t + s;
return 0;
}
private static boolean IsPairLoop(int first, int second) {
int s = first+second;
int t_slow = first-second;
int t_fast = t_slow;
while(t_slow != t_fast) {
t_slow = step(s, t_slow);
t_fast = step(s, step(s, s_fast));
}
return t_slow != 0;
}
如果int
可能溢出,则可能需要用long
替换first+second
。
我认为,在满足前提条件的情况下,您永远不会遇到t
变为无穷大的情况(因为|t| < s
总是如此)。但是您可能希望仔细检查,也许在代码中添加某种断言。
答案 2 :(得分:1)
让我们往后一点点。什么可以产生x == y?前一个x和y之间的差必须等于两者中较小者的两倍,即较大者必须是较小者的三倍。到目前为止,不会无限循环的事情:
{n,3n}可以从哪里来?要么
n是一些a> b的差a − b,并且3n = 2b
3(a-b)= 2b
3a-3b = 2b
3a = 5b
a = 5/3 b
一对{m,5/3 m}会在下一步产生{n,3n}。 (m必须被3整除,但是没关系。)
3n对于a> b来说是a − b的差,并且n = 2b
(a − b)/ 3 = 2b
a − b = 6b
a = 7b
另一对{m,7m}是下一步可以产生{n,3n}的其他东西。
更新的列表:
似乎是总结这些最后步骤的好时机。
{n,qn}在以下情况下发生:
n是一些a> b的差a − b,并且qn = 2b
q(a − b)= 2b
qa − qb = 2b
qa =(2 + q)b
a =(2 + q)/ q b
或qn是a − b的差,并且n = 2b
(a − b)/ q = 2b
a − b = 2qb
a =(2q +1)b
因此,如果q = m / n在列表中,则它们也在列表中:
q = 1生成:
q = 3生成:
q = 5/3生成:
q = 7生成:
嗯...这很有趣。让我们按分子对列表进行排序:
基于此模式,我希望接下来会是17/15。用计算机生成按分母排序的列表:
3/1
7/1
15/1
31/1
63/1
5/3
13/3
29/3
61/3
11/5
27/5
59/5
9/7
25/7
57/7
23/9
55/9
21/11
53/11
19/13
51/13
17/15
49/15
47/17
45/19
43/21
41/23
39/25
37/27
35/29
33/31
看起来很像m / n,其中n为奇数,m> n,而m + n为2的幂。因此,一种优化算法的方法是:
private static boolean isPairLoop(int first, int second)
{
if (first == second) return false;
if (first > second) return isPairLoop(second, first);
if (first == 0) return true;
int d = gcd(first, second);
return Integer.bitCount(first / d + second / d) != 1;
}
private static int gcd(int a, int b)
{
return b == 0 ? a : gcd(b, a % b);
}
对bigints的位数取二次时间。
现在,您只需要证明它可以工作。 我希望它能起作用。