Number of coprime pairs in a given segment - number-theory

I am looking for an efficient algorithm that given two positive integers n and m, finds the number of coprime pairs (x,y) such that 1 <= x <= n and 1 <= y <= m. Any ideas?

Why not just use a ternary tree to generate the coprimes? Something like
def gen_coprimes(max_m, max_n):
t1 = [(2,1)]
t2 = [(3,1)]
b1 = lambda m,n:(2*m-n, m)
b2 = lambda m,n:(2*m+n, m)
b3 = lambda m,n:(m+2*n, n)
while t1 or t2:
yield from t1
yield from t2
t1 = [(i,j) for i,j in (f(m,n) for m,n in t1 for f in (b1,b2,b3)) if i<max_m and j<max_n]
t2 = [(i,j) for i,j in (f(m,n) for m,n in t2 for f in (b1,b2,b3)) if i<max_m and j<max_n]
list(gen_coprimes(5,6)) gives [(2, 1), (3, 1), (3, 2), (4, 1), (4, 3)].
Should be somewhere around O(n). Correct me if I'm wrong though, still getting a handle on this big O notation stuff. Algorithm from here: http://en.wikipedia.org/wiki/Coprime_integers#Generating_all_coprime_pairs

Related

is there a way to compare an Array A with another array B where B is xA + y under O(n^2)?

given an array A of size n and another array called B.
B is made of array A and 2 constants x and y where Bi = x * Ai + c (i => 0,1,2, … N-1)
For example,
if A : {3, 1, 5, 7} and x = 1 and y =2, then B : {5, 3, 7 ,9}
question: find out how many times A (j+1), (j+2), …. (n -1) > Bj for j E {0, 1, … n-1 }
Using the sample input above
A: {3,1,5,7}
B: {5,3,7,9}
the answer would be 3 since
when j = 0, A3 > B0 (7>5)
when j = 1, A2 > B1 (5>3) and A3 > B1 (7>3)
since there are only 3 possibilities of A j+1 …. n -1 > Bj for j E {0, 1, … n-1 }, the output will be 3
is there a way to solve this with an algorithm below O(n^2)? thanks!
Why not insert elements of B into an order statistic tree as we iterate on A, and look up for each element we see in A, how many in the tree are lower? At each A_i, make sure all but only elements up to B_(i-1) are in the tree before the lookup. This wouldn't distinguish which Bs have higher A elements, but it seems like the question is only asking for the total number. O(n log n) time, O(n) extra space.

Efficient way to find min(max(A[L], A[L+1],...,A[R]), min(B[L], B[L+1],…, B[R]))

Given 2 array A[N] and B[N]. For each 0 <= L < N <= 5e5, find the maximum value of
min(max(A[L], A[L+1],...,A[R]), min(B[L], B[L+1],…, B[R]))
for L <= R <= N.
ans[L] is the answer for L.
For example,
N = 3
A[3] = {3, 2, 1}
B[3] = {3, 2, 3}
So, the answer is
ans[0] = 3
ans[1] = 2
ans[2] = 1
It is clear that brute-forces can run fast.
Then, I tried using Sparse table, Segment Tree or Binary Indexed Tree (and we don't need to update anything, so I choose Sparse Table). But for each L, we don't know R, so I need to run to the end of the array, and this doesn't different from brute-forces .
Is there any efficient algorithm or data structures for this problem??
P/s: Sorry for my bad English.
Using Sparse table A is monotone increasing, B is monotone decreasing, so we need to find the crossing point to get the max out of their min ...
pseudo python code untested
stA = SparseTable(A);
stB = SparseTable(B);
for (i in range(len(A))
r = len(B)
l = i
a = stA.max(l,r)
b = stB.min(l,r)
# binary search for crossing point
while (l != r)
m = l + (r-l)//2 # integer division
a = stA.max(l,m)
b = stB.min(l,m)
if (b > a)
l = m + 1
else
r = m
ans[i] = min(a,b) # might be off-by-one m?
max(A[L], A[L+1], ..., A[R]) is non-increasing in L and non-decreasing in R. Conversely, min(B[L], B[L+1], ..., B[R]) is non-decreasing in L and non-increasing in R. It follows that the function from L to the argmax in R is non-decreasing. The last ingredient is two queues, one that can report max, one that can report min, to quickly compute the sliding window aggregates.

Minimum steps to reach target, given (1, 0) and operations A = 2A - B or B = 2B - A

I had an interview and I was not able to give a best approach for the problem.
A=1, B=0
- Operation L: A=2A-B
- Operation R: B=2B-A
For each step, only one operation(L or R) is taken place.
For a given number N, what is the minimum number of operations required to make A or B equals to N?
The most important one is an efficiency.
Thanks in advance.
In k operations you can get all values of N in [-(2^k)+1, 2^k].
Notice that abs(A) + abs(B) = 2^k for all possible k paths, and that A & B exactly cover the range [-(2^k)+1, 2^k] in the set of paths of length k.
k=0: (1,0)
k=1: (1,-1), (2,0)
k=2: (1,-3), (2,-2), (3,-1), (4,0)
etc...
Given N we can find the minimum k via log. Then we know the final pair is (N, N - 2^k) (or (N-2^k, N) if N <= 0). It's easy to follow the path back up to k=0 because one of the two elements will be out of range for the next smaller k.
E.g., N = 35.
Log2(35) = 5.13, so we use k=6.
2^6 = 64, so our final pair is (35, -29)
(35,-29) -> (3,-29) -> (3, -13) -> (3, -5) -> (3,-1) -> (1,-1) -> (1,0)
Figuring out k is O(1), finding the path is O(k) which is O(log(abs(N)).
It's not likely you need to prove anything in an interview, but if you did, you could use this:
By observation: A - B = 2^k for k steps observed for small k.
Then via induction: we have some valid (A, B) s.t. A-B = 2^k. Then L gets us (2A-B, B), but 2A-B-B = 2A-2B = 2(A-B) = 2^(k+1) as desired. Similarly for R.
It would be a challenging task for an interview but I would start with the recursion of trying to find the origin from the result. Given valid (A', B), where A' is the target we are after,
A' = 2A - B
for some A, which means that
A = (A' + B) / 2
The latter tells us that all (A' + B) in the path must be divisible by 2, and since the path ends (starts) at 1, all the (A' + B) in it are powers of 2.
Another property we can observe, although it may not be relevant to the solution is that once we switch in the first step to (even, even) or (odd, odd), we cannot switch back.

Number of pairs with a given sum and product

I have an array A along with 3 variables k, x and y.
I have to find number of unordered pairs (i,j) such that the sum of two elements mod k equals x and the product of the same two elements mod k is equal to y. Pairs need not be distinct. In other words, the number of (i,j) so that
(A[i]+A[j])%k == x and (A[i]*A[j])%k == y where 0 <= i < j < size of A.
For example, let A={1,2,3,2,1}, k=2, x=1, y=0. Then the answer is 6, because the pairs are: (1,2), (1,2), (2,3), (2,1), (3,2), and (2,1).
I used a brute force approach, but obviously this is not acceptable.
Modulo-arithmetic has the following two rules:
((a mod k) * (b mod k)) mod k = (a * b) mod k
((a mod k) + (b mod k)) mod k = (a + b) mod k
Thus we can sort all values into a hashtable with separate chaining and k buckets.
Addition
Find m < k, such that for a given n < k: (n + m) mod k = x.
There is exactly one solution to this problem:
if n < x: m < x must hold. Thus m = x - n
if n == x: m = 0
if n > x: we need to find m such that n + m = x + k. Thus m = x + k - n
This way, we can easily determine for each list of values the corresponding values such that for any pair (a, b) of the crossproduct of the two lists (a + b) mod k = x holds.
Multiplication
Multiplication is a bit trickier. Luckily we've already been given the matching congruence-class for addition (see above), which must as well be the matching congruence-class for the multiplication, since both constraints need to hold. To verify that the given congruence-class matches, we only need to check that (n * m) mod k = y (n and m defined as above). If this expression holds, we can build pairs, otherwise no matching elements exist.
Implementation
This would be the working python-code for the above example:
def modmuladd(ls, x, y, k):
result = []
# create tuples of indices and values
indices = zip(ls, range(0, len(ls)))
# split up into congruence classes
congruence_cls = [[] for i in range(0, k)]
for p in indices:
congruence_cls[p[0] % k].append(p)
for n in range(0, k):
# congruence class to match addition
if n < x:
m = x - n
elif n == x:
m = 0
else:
m = x + k - n
# check if congruence class matches for multiplication
if (n * m) % k != y or len(congruence_cls[m]) == 0:
continue # no matching congruence class
# add matching tuple to result
result += [(a, b) for a in congruence_cls[n] for b in congruence_cls[m] if a[1] <= b[1]]
result += [(a, b) for a in congruence_cls[m] for b in congruence_cls[n] if a[1] <= b[1]]
# sort result such according to indices of first and second element, remove duplicates
sorted_res = sorted(sorted(set(result), key=lambda p: p[1][1]), key=lambda p: p[0][1])
# remove indices from result-set
return [(p[0][0], p[1][0]) for p in sorted_res]
Note that sorting and elimination of duplicates is only required since this code concentrates on the usage of congruence-classes than perfect optimization. This example can be easily tweaked to provided ordering without the sorting by minor modifications.
Test run
print(modmuladd([1, 2, 3, 2, 1], 1, 0, 2))
Output:
[(1, 2), (1, 2), (2, 3), (2, 1), (3, 2), (2, 1)]
EDIT:
Worst-case complexity of this algorithm is still O(n^2), due to the fact that building all possible pairs of a list of size n is O(n^2). With this algorithm however the search for matching pairs can be cut down to O(k) with O(n) preprocessing. Thus counting resulting pairs can be done in O(n) with this approach. Assuming the numbers are distributed equally over the congruence-classes, this algorithm could build all pairs that are part of the solution-set in O(n^2/k^2).
EDIT 2:
An implementation that only counts would work like this:
def modmuladdct(ls, x, y, k):
result = 0
# split up into congruence classes
congruence_class = {}
for v in ls:
if v % k not in congruence_class:
congruence_class[(v % k)] = [v]
else:
congruence_class[v % k].append(v)
for n in congruence_class.keys():
# congruence class to match addition
m = (x - n + k) % k
# check if congruence class matches for multiplication
if (n * m % k != y) or len(congruence_class[m]) == 0:
continue # no matching congruence class
# total number of pairs that will be built
result += len(congruence_class[n]) * len(congruence_class[m])
# divide by two since each pair would otherwise be counted twice
return result // 2
Each pair would appear exactly twice in the result: once in-order and once with reversed order. By dividing the result by two this is being corrected. Runtime is O(n + k) (assuming dictionary-operations are O(1)).
The number of loops is C(2, n) = 5!/(2!(5-2)! = 10 loops in your case, and there is nothing magic that would drastically reduce the number of loops.
In JS you can do:
A = [1, 2, 3, 2, 1];
k = 2;
x = 1;
y = 0;
for(i=0; i<A.length; i++) {
for(j=i+1; j<A.length; j++) {
if ((A[i]+A[j])%k !== x) {
continue;
}
if ((A[i]*A[j])%k !== y) {
continue;
}
console.log('('+A[i]+', '+A[j]+')');
}
}
Ignoring A, we can find all solutions of n * (x - n) == y mod k for 0 <= n < k. That's a simple O(k) algorithm -- check each such n in turn.
We can count, for each n, how often A[i] == n, and then reconstruct the counts of pairs. For if cs is an array of these counts, and n is a solution of n * (x - n) == y mod k, then there's cs[n] * cs[(x-n)^k] pairs of things in A that solve our equations corresponding to this n. To avoid double counting we only count n such that n < (x - n) % k.
def count_pairs(A, k, x, y):
cs = [0] * k
for a in A:
cs[a % k] += 1
pairs = ((i, (x-i)%k) for i in xrange(k) if i * (x-i) % k == y)
return sum(cs[i] * cs[j] for i, j in pairs if i < j)
print count_pairs([1, 2, 3, 2, 1], 2, 1, 0)
Overall, this constructs the counts in O(|A|) time, and the remaining code runs in O(k) time. It uses O(k) space.

Finding vertices in an ordered set of a complete graph

Problem: For an ordered set of edges E of a complete graph Kn, given an edge Ei, find the edge's vertices (v, w)_Ei.
Note: This is likely not a problem specific to graph theory, although it was chosen to express the problem solely because of familiarity. Apologies for any incorrect notation introduced.
Suppose that constructed from a complete graph K5 consisting of vertices 1, 2, 3, 4, 5, we have an ordered set E of the graph's edges, totalling 10 edges. The set E is known to always be ordered as follows:
Ei = (0 < v < n, v < w =< n)
E1 = (1, 2)
E2 = (1, 3)
E3 = (1, 4)
E4 = (1, 5)
E5 = (2, 3)
E6 = (2, 4)
E7 = (2, 5)
E8 = (3, 4)
E9 = (3, 5)
E10 = (4, 5)
For any given Ei, we must now find the vertices (v, w)_Ei using i alone. For example, given 6 we should obtain (2, 4).
Update:
Another, perhaps simpler way of expressing this problem is:
n = 5
i = 0
for v = 1 to n - 1
for w = v + 1 to n
i++
print "E" + i + " = " + v + ", " w
print "E6 = " + findV(6) + ", " + findW(6)
How is this done?
To solve the problem in closed form, we need the formula for the sum of first k numbers: 1 + 2 + ... + k = (k + 1) * k / 2. This gives us a mapping from edge (i, j) to edge index:
from math import ceil, sqrt
def edge_to_index((i, j)):
return n * (i - 1) + j - i * (i + 1) / 2
We can derive the inverse mapping:
def index_to_edge(k, n):
b = 1.0 - 2 * n
i = int(ceil((-b - sqrt(b**2 - 8 * k)) / 2))
j = k - n * (i - 1) + i * (i + 1) / 2
return (i, j)
A test:
n = 5
print "Edge to index and index to edge:"
for i in range(1, n + 1):
for j in range(i + 1, n + 1):
k = edge_to_index((i, j))
print (i, j), "->", k, "->", index_to_edge(k, n)
The output:
Edge to index and index to edge:
(1, 2) -> 1 -> (1, 2)
(1, 3) -> 2 -> (1, 3)
(1, 4) -> 3 -> (1, 4)
(1, 5) -> 4 -> (1, 5)
(2, 3) -> 5 -> (2, 3)
(2, 4) -> 6 -> (2, 4)
(2, 5) -> 7 -> (2, 5)
(3, 4) -> 8 -> (3, 4)
(3, 5) -> 9 -> (3, 5)
(4, 5) -> 10 -> (4, 5)
Let me restate the question I think you're asking so that if this is totally off-topic, you can let me know:
Given an integer k and the series (1, 2), (1, 3), ..., (1, k), (2, 3), (2, 4), ..., (2, k), (3, 4), ..., (k - 1, k) and an index n, return the value of the nth term of this series.
Here's a simple algorithm to solve this problem that is probably not asymptotically optimal. Notice that the first (k - 1) of the pairs start with 1, the next (k - 2) start with 2, the next (k - 3) with 3, etc. To determine what the value of the first element in the pair is, you can keep adding up these numbers (k - 1) + (k - 2) + ... until you end up with a value that is greater than or equal to your index. The number of times you could do this, plus one, gives you your first number:
E1 = (1, 2)
E2 = (1, 3)
E3 = (1, 4)
E4 = (1, 5)
E5 = (2, 3)
E6 = (2, 4)
E7 = (2, 5)
E8 = (3, 4)
E9 = (3, 5)
E10 = (4, 5)
Here, k = 5. To find the first number of term 8, we first add k - 1 = 4, which is less than eight. We then add k - 2 = 3 to get 7, which is still less than eight. However, adding k - 3 = 2 would give us nine, which is greater than eight, and so we stop. We added two numbers together, and so the first number must be a 3.
Once we know what the first number is, you can get the second number quite easily. When doing the step to get the first number, we're essentially listing off the indices of the pairs where the first number changes. For example, in our above case, we had the series 0, 4, 7. Adding one to each of these gives 1, 5, 8, which are indeed the first pairs that start with the numbers 1, 2, and 3, respectively. Once you know what the first number is, you also know where pairs with that number start, and so you can subtract out the index of your number from that position. This tells you, with a zero-index, how many steps you've taken forward from that element. Moreover, you know what the second value of that first element is, because it's one plus the first element, and so you can say that the second value is given by the first number, plus one, plus the number of steps your index is beyond the first pair starting with the given number. In our case, since we are looking at index 8 and we know the first pair starting with a three is at position 8, we get that the second number is 3 + 1 + 0 = 4, and our pair is (3, 4).
This algorithm is actually pretty fast. Given any k, this algorithm takes at most k steps to complete, and so runs in O(k). Compare this to the naive approach of scanning everything, which takes O(k2).
To make my life easier, I'm going to do my math 0-based, not 1-based as in your question.
First, we derive a formula for the index of the term (v,v+1) (the first that starts with v). This is just the arithmetic sum of n-1 + n-2 + ... + n-v, which is v(2n-v-1)/2.
So to find v given the index i, just solve the equation v(2n-v-1)/2 <= i for the largest integral v. Binary search would work well, or you could solve the quadratic using the quadratic formula and round down (maybe, have to think if that ends up working).
Finding the W is easy given V:
findW(i):
v = findV(i)
i_v = v(2n-v-1)/2
return i - i_v + 1
Well, the simple way is to loop through and subtract values corresponding to the first vertex, as follows (in python):
def unpackindex(i,n):
for v in range(1,n):
if v+i<=n: return (v,v+i)
i-= n-v
raise IndexError("bad index")
If you're looking for a closed-form formula, rather than an algorithm, you will need to do a square root at some point, so it is likely to be messy and somewhat slow (though not as slow as the above loop, for large enough n...). For moderate values of n, you might want to consider a precomputed lookup table, if performance is important.

Resources