7

I have an arbitrary number $x$. I would like to compute a number that is coprime to $x$ that's close(ish) to the square root of $x$. I don't need to find them all, and factoring $x$ is expensive. I just need one number. I could also check a few primes near the square root of $x$, but computing primes and storing primes is expensive.

Constant time and space, preferably.

Him
  • 483
  • 1
    Constant time and space :O – Kenny Lau Sep 15 '17 at 15:49
  • The probability of two randomly chosen integers being coprime is 61% ($6/\pi^2$), but I wouldn't know if you fix one integers. – Kenny Lau Sep 15 '17 at 15:51
  • 2
    Factoring is costly, but computing a gcd is not. I'd start at the square root and search for nearby numbers with gcd $=1$. – lulu Sep 15 '17 at 15:51
  • @KennyLau I suppose gcd is log(n). That's fine. However, there are, for example, primes of particular classes that are just "known", e.g. Mersenne primes. One could take a couple of those and check a simple division. Mersenne primes are too sparse to get near the sqrt of an arbitrary number though. – Him Sep 15 '17 at 15:52
  • $6/\pi^2$.... isn't that $\sum 1/x^2$? That's a crazy coincidence... – Him Sep 15 '17 at 15:54
  • Special primes are way too special to be of much use for these purposes. I would just run Euclid on a whole bunch of numbers near the square root. As a bonus, if you get a non-trivial gcd you can partially factor your number. That should help. – lulu Sep 15 '17 at 15:55
  • 4
    @Scott It isn't a coincidence. $\displaystyle \prod_{p \in \Bbb P} \left( 1 - \dfrac 1 {p^n} \right) = \dfrac 1 {\zeta(n)}$ for arbitrary $n$, and $n=2$ in this case. (Also, $\sum 1/x^2 = \pi^2/6$.) – Kenny Lau Sep 15 '17 at 16:02

2 Answers2

4

Since calculating the gcd of two arbitrary number isn't expensive, you can practically brute force it.

Algorithm:

  1. Generate a random number n.
  2. Find its integer-square-root r.
  3. Check if gcd(n,r)=1. If yes, return r and exit.
  4. If no, add 1 to r, and repeat step 3.

Python:

import random
import time
start = time.time()
bit = 512 # even
num = 1
for _ in range(bit): num = num * 2 + random.randrange(2)
end = time.time()
print("random number:")
print(num)
print("time for generating random number:       %f"%(end-start))

# integer square root (wiki)
start = time.time()
res = 0
bit = 2**bit
while bit:
    if num >= res + bit:
        num -= res + bit
        res = (res >> 1) + bit
    else:
        res >>= 1
    bit >>= 2
end = time.time()
print("time for generating itneger square root: %f"%(end-start))

def gcd(a,b):
    while a: a,b = b%a,a
    return b

start = time.time()
while gcd(res,num) != 1: res += 1
end = time.time()
print("time for generating co-prime number:     %f"%(end-start))

print(res)

Does it in fraction of a second.

Try it online!

Sample output:

random number:
16956998685025525617332092680088906859010597516989049974644188276809460728386128015966080402491132114558031760245789600047216269236212122151725198496639367
time for generating random number:       0.000913
time for generating itneger square root: 0.000193
time for generating co-prime number:     0.000031
130219041176878297644835828972023265387463111246248427493495319607240172982284
Kenny Lau
  • 25,655
  • 33
  • 78
4

The previous answers iteratively searches for a coprime, and running in O(coprime gap after num) gcd invokes. A long, long, long processing time could be needed with this algorithm.

Take a number like 47897850, its integer square root is 6920, and it can be verified that 23 gcd calculations are needed before finding 6943 as the first coprime to 47897850.

gcd( 47897850 , 6920 ) = 10
gcd( 47897850 , 6921 ) = 3
gcd( 47897850 , 6922 ) = 2
gcd( 47897850 , 6923 ) = 7
gcd( 47897850 , 6924 ) = 6
gcd( 47897850 , 6925 ) = 25
gcd( 47897850 , 6926 ) = 2
gcd( 47897850 , 6927 ) = 3
gcd( 47897850 , 6928 ) = 2
gcd( 47897850 , 6929 ) = 13
gcd( 47897850 , 6930 ) = 2310
gcd( 47897850 , 6931 ) = 29
gcd( 47897850 , 6932 ) = 2
gcd( 47897850 , 6933 ) = 3
gcd( 47897850 , 6934 ) = 2
gcd( 47897850 , 6935 ) = 5
gcd( 47897850 , 6936 ) = 6
gcd( 47897850 , 6937 ) = 7
gcd( 47897850 , 6938 ) = 2
gcd( 47897850 , 6939 ) = 3
gcd( 47897850 , 6940 ) = 10
gcd( 47897850 , 6941 ) = 11
gcd( 47897850 , 6942 ) = 78
gcd( 47897850 , 6943 ) = 1

And the following numbers full of small factors are an example of increased calculation costs when trying to find the very first coprime to num larger than integer sqrt(num)

4 = 2^2
12 = 2^2 × 3
6 = 2 × 3
585 = 3^2 × 5 × 13
42 = 2 × 3 × 7
8400 = 2^4 × 3 × 5^2 × 7
930 = 2 × 3 × 5 × 31
1110 = 2 × 3 × 5 × 37
1974 = 2 × 3 × 7 × 47
13860 = 2^2 × 3^2 × 5 × 7 × 11
44310 = 2 × 3 × 5 × 7 × 211
87360 = 2^6 × 3 × 5 × 7 × 13
86940 = 2^2 × 3^3 × 5 × 7 × 23
3361050 = 2 × 3^2 × 5^2 × 7 × 11 × 97
8873304 = 2^3 × 3 × 11 × 19 × 29 × 61
4774770 = 2 × 3^2 × 5 × 7 × 11 × 13 × 53
13988370 = 2 × 3 × 5 × 11 × 19 × 23 × 97
47940480 = 2^7 × 3^2 × 5 × 7 × 29 × 41
42286650 = 2 × 3 × 5^2 × 7 × 17 × 23 × 103
50854650 = 2 × 3 × 5^2 × 7^2 × 11 × 17 × 37
60233040 = 2^4 × 3^2 × 5 × 7 × 17 × 19 × 37
52644900 = 2^2 × 3 × 5^2 × 7 × 11 × 43 × 53
47897850 = 2 × 3 × 5^2 × 7 × 11^2 × 13 × 29
....

The maximum "coprime gap" size after a number n seems to grow in O(ln(n))

It would be faster to iterate constructively by opportunistically accumulate the factors of the input number when they are the outputs of gcd. Worst case, this will run in less than O(#factors num) gcd invokes.

I modified the python script as follows:

import random
import time

bit = 512 # even

start = time.time() pow = 2**bit num = random.randrange(pow >> 1, pow - 1) end = time.time()

print("random number num:") print(num) print("time for generating random number: %f"%(end-start)) print()

def sqrt(a,b): # integer square root (wiki) res = 0 pow = 2**b num = a while pow: if num >= res + pow: num -= res + pow res = (res >> 1) + pow else: res >>= 1 pow >>= 2 return res

start = time.time() sqrn = sqrt(num, bit) end = time.time()

print("square root number sqrt(num):") print(sqrn) print("square of squareroot number:") print(sqrn*sqrn) print("time for generating integer square root: %f"%(end-start)) print()

def gcd(a,b): while a: a,b = b%a,a return b

def coprime(a, b): # constructively compute the coprime of a given number a, # starting from some arbitrary randomness # input b : any random start number # input a : number # output res : with res < a, coprime to a and close(ish) to b b = 1 + (b % (a - 1)) # make sure 0 < b < a factors = 1 res = b while True: g = gcd(res,a) if (g == 1): break; factors *= g # accumulate multiple factors of a found in b res = b + factors - 1

# now res is coprime to a, and res % a is also coprime to a, and res &lt; a
res %= a
return res

start = time.time() c = coprime(num, sqrn); end = time.time()

print("number coprime to num close(ish) to sqrt(num):") print(c) print("verify with gcd(num, coprime to num)") print(gcd(num, c)) print("time for generating co-prime number: %f"%(end-start)) print()

With the modified algorithm, the output of the python script is

random number num:
11185931069012882618484249292312310042666137944360829367669494242527613887036910725486770684387712709721024564190939992975044065122017303009053576484841785
time for generating random number:       0.000012

square root number sqrt(num): 105763562104407596481968445281612018565294009154907045716655815866995991158313 square of squareroot number: 11185931069012882618484249292312310042666137944360829367669494242527613887036832565809732701215533464477407018527101343984451072439242764775553671429005969 time for generating integer square root: 0.000097

number coprime to num close(ish) to sqrt(num): 105763562104407596481968445281612018565294009154907045716655815866995991158404 verify with gcd(num, coprime to num) 1 time for generating co-prime number: 0.000057

Then

6 = 2 × 3 <-- smallest number with 2 gcd calculations
78 = 2 × 3 × 13 <-- smallest number with 3 gcd calculations
13464 = 2^3 × 3^2 × 11 × 17 <-- smallest number with 4 gcd calculations
396825 = 3 × 5^2 × 11 × 13 × 37 <-- smallest number with 5 gcd calculations
13679820 = 2^2 × 3^3 × 5 × 72 × 11 × 47  <-- smallest number with 6 gcd calculations
10360350 = 2 × 3^2 × 5^2 × 7 × 11 × 13 × 23 <-- smallest number with 7 gcd calculations
4894696950 = 2 × 3 × 5^2 × 11 × 13 × 17 × 31 × 433 <-- smallest number with 8 gcd calculations
....
15359277570 = 2 × 3 × 5 × 7 × 13 × 19 × 37 × 53 × 151
....

The worst case of complexity of the modified algorithm seems to grow with the number of distinct prime factors in O(ln(ln(num))).

Pierre
  • 198