Just a bit of a historical note to say...
Peter Shor discovered his quantum factoring algorithm in 1994, after reading an earlier paper by Dan Simon solving a similar problem that was: a) simpler; b) practically useless. (It was useful in that it inspired Shor!) We didn't end up talking about Simon's problem/algorithm.
After Shor came up with his algorithm, before he published it, he described it in a few talks and sent some drafts to a few people. So word kind of got out that Shor had solved factoring on a quantum computer.
One person who heard about this was Alexei Kitaev, a physicist working in Russia. He didn't know anything about how Shor did it, but having heard that it was possible, he just decided to figure it out himself -- which he did. Interestingly, he actually came up with a different method for factoring efficiently with a quantum computer.>
Well, the business about reducing to "finding L" was the same, but Kitaev basically invented the Revolver Resolver method that we saw in class. Shor instead had used the "Quantum Fourier Transform", which we didn't describe in class.
So in fact, we didn't see Shor's quantum factoring algorithm in class, we actually saw Kitaev's :)
(Though, if you really dig in deep, you can see that they're ultimately basically the same algorithm, just presented in two very different ways.)
Suppose we have Add U(x) To y
operation, it is possible to turn into y = U(x)
operation.
def U(x):
Make a1, a2, a3, ...
Add U(x) To a1, a2, a3, ...
Swap x with a1, a2, a3, ...
Add U-1(x) to a1, a2, a3, ...
Now we will show how we can use L to factor F. For simplicity, we assume F = PQ for P, Q large prime numbers.
def mod_inv(A, M):
m0 = M
y = 0
x = 1
if (M == 1): return 0
while (A > 1):
# q is quotient
q = A // M
t = M
# m is remainder now, process
# same as Euclid's algo
M = A % M
A = t
t = y
# Update x and y
y = x - q * y
x = t
# Make x positive
if (x < 0):
x = x + m0
return x
def gcd(x, y):
while(y):
x, y = y, x % y
return abs(x)
# This simulates finding the length of cycle of
# U(x) = M * x mod F
def findL(F, M):
i = None
count = 0
while(i != 1):
if i == None:
i = 1
i = (M * i) % F
count = count + 1
return count
def factor(F, M):
print("M = {} mod {}".format(M, F))
if gcd(M, F) != 1:
print("Modular inverse W*M=1 mod F is not possible -> {} = {}*{}".format(F, M, F/M))
return M, F/M
W = mod_inv(M, F)
L = findL(F, M)
lucky_even = L % 2 == 0
if not lucky_even:
print("found W={}, L={} -> Unlucky (L%2!=0)".format(W, L))
return -1, -1
S = (M ** (L/2)) % F
assert (S ** 2) % F == 1 # It must be S^2 = (M^(L/2))^2 = M^L = 1 mod F
lucky_mod = S != F - 1
if not lucky_mod:
print("found W={}, L={}, S={} -> Unlucky ({} = - 1 mod {})".format(W, L, S, S, F))
return -1, -1
# Then it must be S+1 != 0 mod F and S-1 != 0 mod F
# But then S^2 = 1 mod F, so (S^2 - 1) = 0 mod F, so (S-1)(S+1) = 0 mod F
# Therefore (S-1)(S+1) is a multiple of PQ
P = gcd(S-1, F)
Q = gcd(S+1, F)
print("found W={}, L={}, S={} -> {} = {}*{}".format(W, L, S, F, P, Q))
# We guarantee unlucky < 50% for all choices of F
# And in practice, a random F will be very very lucky
return P, Q
Now if we execute:
factor(35, 2)
factor(35, 3)
factor(35, 4)
factor(35, 5)
factor(35, 6)
factor(35, 11)
factor(35, 19)
It will print out:
M = 2 mod 35
found W=18, L=12, S=29.0 -> 35 = 7.0*5.0
M = 3 mod 35
found W=12, L=12, S=29.0 -> 35 = 7.0*5.0
M = 4 mod 35
found W=9, L=6, S=29.0 -> 35 = 7.0*5.0
M = 5 mod 35
Modular inverse W*M=1 mod F is not possible -> 35 = 5*7.0
M = 6 mod 35
found W=6, L=2, S=6.0 -> 35 = 5.0*7.0
M = 11 mod 35
found W=16, L=3 -> Unlucky (L%2!=0)
M = 19 mod 35
found W=24, L=6, S=34.0 -> Unlucky (34.0 = - 1 mod 35)
Table of Content