C Looooops
Description
A Compiler Mystery: We are given a C-language style for loop of type
for (variable = A; variable != B; variable += C) statement; I.e., a loop which starts by setting variable to value A and while variable is not equal to B, repeats statement followed by increasing the variable by C. We want to know how many times does the statement get executed for particular values of A, B and C, assuming that all arithmetics is calculated in a k-bit unsigned integer type (with values 0 <= x < 2 k) modulo 2 k. Input
The input consists of several instances. Each instance is described by a single line with four integers A, B, C, k separated by a single space. The integer k (1 <= k <= 32) is the number of bits of the control variable of the loop and A, B, C (0 <= A, B, C < 2
k) are the parameters of the loop.
The input is finished by a line containing four zeros. Output
The output consists of several lines corresponding to the instances on the input. The i-th line contains either the number of executions of the statement in the i-th instance (a single integer number) or the word FOREVER if the loop does not terminate.
Sample Input 3 3 2 16 3 7 2 16 7 3 2 16 3 4 2 16 0 0 0 0 Sample Output 0 2 32766 FOREVER Source |
提示
题意:
给出A,B,C(0<=A,B,C<2^k),k(1<=k<=32)。经过几次循环才能使A=B,用for循环写成:for(i=A;B!=i;i=i+C),如果数值超出2^k,那就像电脑从0开始计数,比如k=2,运行后A变成5,那么2^2<5,5转化为1。如果永远不能跳出循环输出"FOREVER"。
思路:
首先我们可以写出Cx=(B-A+2^k)%2^k
x=(B-A+2^k)%2^k/C
之后。。。还是不能得出答案
因为有不能跳出循环这一情况,需要用扩展欧几里得算法(来源于书):
形如ax+by=c的不定方程称为二元一次不定式方程。显然(1)a=0或b=0时,方程的解确定。(2)c不是gcd(a,b)的倍数时,方程无解。
因此,只考虑ab!=0且gcd(a,b)能整除c的情况。
求最大公约数的过程:
r0=r1*q1+r2
r1=r2*q2+r3
......
rk-1=rk*qk
由倒数第二个式子往回推演,将倒数第二个式子变形得到
gcd(a,b)=rn-1*x1+rn-2*y1
联立倒数第三个式子消去rn-1得到
gcd(a,b)=rn-2*x2+rn-3*y2
以此类推,一直得到
gcd(a,b)=a*x+b*y
如此便得出了ax+by=gcd(a,b)的解,那么c是gcd(a,b)的倍数时的解也就很明显了。
那么要怎么套呢?
刚推出的方程简化为Cx=(B-A)mod2^k,这里我们称x是关于2^k的乘法逆元,表达式等价与Cx+(2^k)*y=(B-A)。
之后就好做了吧。
示例程序
Source Code
Problem: 2115 Code Length: 760B
Memory: 388K Time: 0MS
Language: GCC Result: Accepted
#include <stdio.h>
long long x,y;
long long f(long long a,long long b)
{
long long t,d;
if(b==0)
{
x=1;
y=0;
return a;
}
d=f(b,a%b);
t=x;
x=y;
y=t-a/b*y;
return d;
}
int main()
{
long long k,i,d,A,B,C,a,b,c;
scanf("%lld %lld %lld %lld",&A,&B,&C,&k);
while(A!=0||B!=0||C!=0||k!=0)
{
b=1;
for(i=1;k>=i;i++)
{
b=b*2;
}
a=C;
c=B-A;
d=f(a,b); //扩展欧几里得算法
if(c%d!=0)
{
printf("FOREVER\n");
}
else
{
x=(x*c/d)%b; //完全循环一次就够了,所以有c/d
b=b/d;
x=(x%b+b)%b; //因为都是非负数所以要进行转化
printf("%lld\n",x);
}
scanf("%lld %lld %lld %lld",&A,&B,&C,&k);
}
return 0;
}