How can I find the minimum index of the array in this case? - algorithm

We are given an array with n values.
Example: [1,4,5,6,6]
For each index i of the array a ,we construct a new element of array b such that,
b[i]= [a[i]/1] + [a[i+1]/2] + [a[i+2]/3] + ⋯ + [a[n]/(n−i+1)] where [.] denotes the greatest integer function.
We are given an integer k as well.
We have to find the minimum i such that b[i] ≤ k.
I know the brute-force O(n^2) algorithm (to create the array - 'b'), can anybody suggest a better time complexity and way solve it?
For example, for the input [1,2,3],k=3, the output is 1(minimum-index).
Here, a[1]=1; a[2]=2; a[3]=3;
Now, b[1] = [a[1]/1] + [a[2]/2] + [a[3]/3] = [1/1] + [2/2] + [3/3] = 3;
b[2] = [a[2]/1] + [a[3]/2] = [2/1] + [3/2] = 3;
b[3] = [a[3]/1] = [3/1] = 3 (obvious)
Now, we have to find the index i such that b[i]<=k , k='3' , also b[1]<=3, henceforth, 1 is our answer! :-)
Constraints : - Time limits: -(2-seconds) , 1 <= a[i] <= 10^5, 1 <=
n <= 10^5, 1 <= k <= 10^9

Here's an O(n √A)-time algorithm to compute the b array where n is the number of elements in the a array and A is the maximum element of the a array.
This algorithm computes the difference sequence of the b array (∆b = b[0], b[1] - b[0], b[2] - b[1], ..., b[n-1] - b[n-2]) and derives b itself as the cumulative sums. Since the differences are linear, we can start with ∆b = 0, 0, ..., 0, loop over each element a[i], and add the difference sequence for [a[i]], [a[i]/2], [a[i]/3], ... at the appropriate spot. The key is that this difference sequence is sparse (less than 2√a[i] elements). For example, for a[i] = 36,
>>> [36//j for j in range(1,37)]
[36, 18, 12, 9, 7, 6, 5, 4, 4, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
>>> list(map(operator.sub,_,[0]+_[:-1]))
[36, -18, -6, -3, -2, -1, -1, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
We can derive the difference sequence from a subroutine that, given a positive integer r, returns all maximal pairs of positive integers (p, q) such that pq ≤ r.
See complete Python code below.
def maximal_pairs(r):
p = 1
q = r
while p < q:
yield (p, q)
p += 1
q = r // p
while q > 0:
p = r // q
yield (p, q)
q -= 1
def compute_b_fast(a):
n = len(a)
delta_b = [0] * n
for i, ai in enumerate(a):
previous_j = i
for p, q in maximal_pairs(ai):
delta_b[previous_j] += q
j = i + p
if j >= n:
break
delta_b[j] -= q
previous_j = j
for i in range(1, n):
delta_b[i] += delta_b[i - 1]
return delta_b
def compute_b_slow(a):
n = len(a)
b = [0] * n
for i, ai in enumerate(a):
for j in range(n - i):
b[i + j] += ai // (j + 1)
return b
for n in range(1, 100):
print(list(maximal_pairs(n)))
lst = [1, 34, 3, 2, 9, 21, 3, 2, 2, 1]
print(compute_b_fast(lst))
print(compute_b_slow(lst))

This probably cannot reach the efficiency of David Eisenstat's answer but since I spent quite a long time figuring out an implementation, I thought I'd leave it up anyway. As it is, it seems about O(n^2).
The elements of b[i] may be out of order, but sections of them are not:
[a[1]/1] + [a[2]/2] + [a[3]/3]
|------ s2_1 -----|
|-s1_1-|
[a[2]/1] + [a[3]/2]
|------ s2_2 -----|
|-s1_2-|
[a[3]/1]
|-s1_3-|
s2_1 < s2_2
s1_1 < s1_2 < s1_3
Binary search for k on s1. Any result with an s1_i greater than k will rule out a section of ordered rows (rows are b_is).
Binary search for k on s2 on the remaining rows. Any result with an s2_i greater than k will rule out a section of ordered rows (rows are b_is).
This wouldn't help much since in the worst case, we'd have O(n^2 * log n) complexity, greater than O(n^2).
But we can also search horizontally. If we know that b_i ≤ k, then it will rule out both all rows with greater or equal length and the need to search smaller s(m)s, not because smaller s(m)s cannot produce a sum >= k, but because they will necessarily produce one with a higher i and we are looking for the minimum i.
JavaScript code:
var sum_width_iterations = 0
var total_width_summed = 0
var sum_width_cache = {}
function sum_width(A, i, width){
let key = `${i},${width}`
if (sum_width_cache.hasOwnProperty(key))
return sum_width_cache[key]
sum_width_iterations++
total_width_summed += width
let result = 0
for (let j=A.length-width; j<A.length; j++)
result += ~~(A[j] / (j + 1 - i))
return sum_width_cache[key] = result
}
function get_b(A){
let result = []
A.map(function(a, i){
result.push(sum_width(A, i, A.length - i))
})
return result
}
function find_s_greater_than_k(A, width, low, high, k){
let mid = low + ((high - low) >> 1)
let s = sum_width(A, mid, width)
while (low <= high){
mid = low + ((high - low) >> 1)
s = sum_width(A, mid, width)
if (s > k)
high = mid - 1
else
low = mid + 1
}
return [mid, s]
}
function f(A, k, l, r){
let n = A.length
if (l > r){
console.log(`l > r: l, r: ${l}, ${r}`)
return [n + 1, Infinity]
}
let width = n - l
console.log(`\n(call) width, l, r: ${width}, ${l}, ${r}`)
let mid = l + ((r - l) >> 1)
let mid_width = n - mid
console.log(`mid: ${mid}`)
console.log('mid_width: ' + mid_width)
let highest_i = n - mid_width
let [i, s] = find_s_greater_than_k(A, mid_width, 0, highest_i, k)
console.log(`hi_i, s,i,k: ${highest_i}, ${s}, ${i}, ${k}`)
if (mid_width == width)
return [i, s]
// either way we need to look left
// and down
console.log(`calling left`)
let [li, ls] = f(A, k, l, mid - 1)
// if i is the highest, width is
// the width of b_i
console.log(`got left: li, ls, i, high_i: ${li}, ${ls}, ${i}, ${highest_i}`)
if (i == highest_i){
console.log(`i == highest_i, s <= k: ${s <= k}`)
// b_i is small enough
if (s <= k){
if (ls <= k)
return [li, ls]
else
return [i, s]
// b_i is larger than k
} else {
console.log(`b_i > k`)
let [ri, rs] = f(A, k, mid + 1, r)
console.log(`ri, rs: ${ri}, ${rs}`)
if (ls <= k)
return [li, ls]
else if (rs <= k)
return [ri, rs]
else
return [i, s]
}
// i < highest_i
} else {
console.log(`i < highest_i: high_i, i, s, li, ls, mid, mid_width, width, l, r: ${highest_i}, ${i}, ${s}, ${li}, ${ls}, ${mid}, ${mid_width}, ${width}, ${l}, ${r}`)
// get the full sum for this b
let b_i = sum_width(A, i, n - i)
console.log(`b_i: ${b_i}`)
// suffix sum is less than k
// so we cannot rule out either side
if (s < k){
console.log(`s < k`)
let ll = l
let lr = mid - 1
let [lli, lls] = f(A, k, ll, lr)
console.log(`ll, lr, lli, lls: ${ll}, ${lr}, ${lli}, ${lls}`)
// b_i is a match so we don't
// need to look to the right
if (b_i <= k){
console.log(`b_i <= k: i, b_i: ${i}, ${b_i}`)
if (lls <= k)
return [lli, lls]
else
return [i, b_i]
// b_i > k
} else {
console.log(`b_i > k: i, b_i: ${i}, ${b_i}`)
let rl = mid + 1
let rr = r
let [rri, rrs] = f(A, k, rl, rr)
console.log(`rl, rr, rri, rrs: ${rl}, ${rr}, ${rri}, ${rrs}`)
// return the best of right
// and left sections
if (lls <= k)
return [lli, lls]
else if (rrs <= k)
return [rri, rrs]
else
return [i, b_i]
}
// suffix sum is greater than or
// equal to k so we can rule out
// this and all higher rows (`b`s)
// that share this suffix
} else {
console.log(`s >= k`)
let ll = l
// the suffix rules out b_i
// and above
let lr = i - 1
let [lli, lls] = f(A, k, ll, lr)
console.log(`ll, lr, lli, lls: ${ll}, ${lr}, ${lli}, ${lls}`)
let rl = highest_i + 1
let rr = r
let [rri, rrs] = f(A, k, rl, rr)
console.log(`rl, rr, rri, rrs: ${rl}, ${rr}, ${rri}, ${rrs}`)
// return the best of right
// and left sections
if (lls <= k)
return [lli, lls]
else if (rrs <= k)
return [rri, rrs]
else
return [i, b_i]
}
}
}
let lst = [1, 2, 3, 1]
// b [3, 3, 3, 1]
lst = [ 1, 34, 3, 2, 9, 21, 3, 2, 2, 1]
// b [23, 41, 12, 13, 20, 22, 4, 3, 2, 1]
console.log(
JSON.stringify(f(lst, 20, 0, lst.length)))
console.log(`sum_width_iterations: ${sum_width_iterations}`)
console.log(`total_width_summed: ${total_width_summed}`)

Why should calculating b[i] lead to O(n²)? If i = 1, it takes n steps. If i = n, it takes one step to calculate b[i]...
You could improve your calculation when you abort the sum on the condition Sum > k.
Let a in N^n
Let k in N
for (i1 := 1; i1 <= n; i1++)
b := 0
for (i2 :=i1; i2 <= n; i2++) // This loop is the calculation of b[i]
b := b + ceil(a[i2]/(i2 + 1))
if (b > k)
break
if (i2 == n)
return i1

Related

Trapping Rain Water(algorithm)

That is a question at leetcode website.
https://leetcode-cn.com/problems/trapping-rain-water/
Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining.
I wrote the solution below.
int solution(vector<int>& height) {
int total = 0;
for (auto pos = height.begin(); pos != height.end(); pos++) {
if (*pos <= *(pos + 1))
continue;
for (auto lmaxpos = pos; *pos >= *(pos + 1); pos++) {
total = total + *lmaxpos - *(pos + 1);
for (; *(pos + 1) <= *lmaxpos; pos++) {
total = total + *lmaxpos - *(pos + 1);
if (*pos >= *(pos + 1)) {
total = total - (lmaxpos - pos) * (*lmaxpos - *pos);
break;
}
break;
}
break;
}
}
return total;
}
But after testing, i find that i have made some logical mistakes and i cann't find it out.
kindly ask you for help.
Traverse the array from both directions, storing the max_height seen so far. Now for each index, the water at that index is max(0, min(left_max, right_max) - cur_height).
E.g.,
input: [0,1,0,2,1,0,1,3,2,1,2,1]
max_from_left: [0,1,1,2,2,2,2,3,3,3,3,3]
max_from_right: [3,3,3,3,3,3,3,3,2,2,2,1]
water: [0-0, 1-0, 2-2, 2-1, 2-0, 2-1, 3-3, 2-2, 2-1, 2-2, 1-1]
water: [0, 1, 0, 1, 2, 1, 0, 0, 1, 0, 0]
sum(water) = 6
Here is a commented/explained solution.
Find maximum height of bar from the left end upto an index i in the array \text{left_max}left_max.
Find maximum height of bar from the right end upto an index i in the array \text{right_max}right_max.
Iterate over the \text{height}height array and update ans
from typing import List
def trapping_water_container(list: List) -> int:
l = 0
r = len(list) - 1
total, maxL, maxR = 0, 0, 0
while l < r:
# Identify the pointer with the lesser value
if list[l] <= list[r]:
# Check if the value for the left
# pointer is smaller than MaxLeft
if list[l] < maxL:
# Calculate the volume for this pointer
total += maxL - list[l]
else:
# Update the MaxLeft
maxL = list[l]
l += 1
else:
# Check if the value for the right
# pointer is smaller than MaxRight
if list[r] < maxR:
# Calculate the volume for this pointer
total += maxR - list[r]
else:
# Update the MaxRight
maxR = list[r]
r -= 1
return total

Bounded square sum algorithm

The problem goes as follows:
You are given two arrays of integers a and b, and two integers lower and upper.
Your task is to find the number of pairs (i, j) such that lower ≤ a[i] * a[i] + b[j] * b[j] ≤ upper.
Example:
For a = [3, -1, 9], b = [100, 5, -2], lower = 7, and upper = 99, the output should be boundedSquareSum(a, b, lower, upper) = 4.
There are only four pairs that satisfy the requirement:
If i = 0 and j = 1, then a[0] = 3, b[1] = 5, and 7 ≤ 3 * 3 + 5 * 5 = 9 + 25 = 36 ≤ 99.
If i = 0 and j = 2, then a[0] = 3, b[2] = -2, and 7 ≤ 3 * 3 + (-2) * (-2) = 9 + 4 = 13 ≤ 99.
If i = 1 and j = 1, then a[1] = -1, b[1] = 5, and 7 ≤ (-1) * (-1) + 5 * 5 = 1 + 25 = 26 ≤ 99.
If i = 2 and j = 2, then a[2] = 9, b[2] = -2, and 7 ≤ 9 * 9 + (-2) * (-2) = 81 + 4 = 85 ≤ 99.
For a = [1, 2, 3, -1, -2, -3], b = [10], lower = 0, and upper = 100, the output should be boundedSquareSum(a, b, lower, upper) = 0.
Since the array b contains only one element 10 and the array a does not contain 0, it is not possible to satisfy 0 ≤ a[i] * a[i] + 10 * 10 ≤ 100.
Now, I know there is a brute force way to solve this, but what would be the optimal solution for this problem?
Sort the smaller array using the absolute value of the elements, then for each element in the unsorted array, binary search the interval on the sorted one.
You can break loop when calculation goes higher than upper limit.
I will reduce execution time.
function boundedSquareSum(a, b, lower, upper) {
let result = 0;
a = a.sort((i,j) => Math.abs(i) - Math.abs(j));
b = b.sort((i,j) => Math.abs(i) - Math.abs(j))
for(let i = 0; i < a.length; i++) {
let aValue = a[i] ** 2;
if(aValue > upper) {
break; // Don't need to check further
}
for(let j = 0; j < b.length; j++) {
let bValue = b[j] ** 2;
let total = aValue + bValue;
if(total > upper) {
break; // Don't need to check further
}
if((total >= lower && total <= upper) ) {
result++;
}
}
}
return result;
}

How can I solve this problem using dynamic programming?

Given a list of numbers, say [4 5 2 3], I need to maximize the sum obtained according to the following set of rules:
I need to select a number from the list and that number will be removed.
Eg. selecting 2 will have the list as [4 5 3].
If the number to be removed has two neighbours then I should get the result of this selection as the product of the currently selected number with one of its neighbours and this product summed up with the other neighbour. eg.: if I select 2 then I can have the result of this selction as 2 * 5 + 3.
If I select a number with only one neighbour then the result is the product of the selected number with its neighbour.
When their is only one number left then it is just added to the result till now.
Following these rules, I need to select the numbers in such an order that the result is maximized.
For the above list, if the order of selction is 4->2->3->5 then the sum obtained is 53 which is the maximum.
I am including a program which lets you pass as input the set of elements and gives all possible sums and also indicates the max sum.
Here's a link.
import itertools
l = [int(i) for i in input().split()]
p = itertools.permutations(l)
c, cs = 1, -1
mm = -1
for i in p:
var, s = l[:], 0
print(c, ':', i)
c += 1
for j in i:
print(' removing: ', j)
pos = var.index(j)
if pos == 0 or pos == len(var) - 1:
if pos == 0 and len(var) != 1:
s += var[pos] * var[pos + 1]
var.remove(j)
elif pos == 0 and len(var) == 1:
s += var[pos]
var.remove(j)
if pos == len(var) - 1 and pos != 0:
s += var[pos] * var[pos - 1]
var.remove(j)
else:
mx = max(var[pos - 1], var[pos + 1])
mn = min(var[pos - 1], var[pos + 1])
s += var[pos] * mx + mn
var.remove(j)
if s > mm:
mm = s
cs = c - 1
print(' modified list: ', var, '\n sum:', s)
print('MAX SUM was', mm, ' at', cs)
Consider 4 variants of the problem: those where every element gets consumed, and those where either the left, the right, or both the right and left elements are not consumed.
In each case, you can consider the last element to be removed, and this breaks the problem down into 1 or 2 subproblems.
This solves the problem in O(n^3) time. Here's a python program that solves the problem. The 4 variants of solve_ correspond to none, one or the other, or both of the endpoints being fixed. No doubt this program can be reduced (there's a lot of duplication).
def solve_00(seq, n, m, cache):
key = ('00', n, m)
if key in cache:
return cache[key]
assert m >= n
if n == m:
return seq[n]
best = -1e9
for i in range(n, m+1):
left = solve_01(seq, n, i, cache) if i > n else 0
right = solve_10(seq, i, m, cache) if i < m else 0
best = max(best, left + right + seq[i])
cache[key] = best
return best
def solve_01(seq, n, m, cache):
key = ('01', n, m)
if key in cache:
return cache[key]
assert m >= n + 1
if m == n + 1:
return seq[n] * seq[m]
best = -1e9
for i in range(n, m):
left = solve_01(seq, n, i, cache) if i > n else 0
right = solve_11(seq, i, m, cache) if i < m - 1 else 0
best = max(best, left + right + seq[i] * seq[m])
cache[key] = best
return best
def solve_10(seq, n, m, cache):
key = ('10', n, m)
if key in cache:
return cache[key]
assert m >= n + 1
if m == n + 1:
return seq[n] * seq[m]
best = -1e9
for i in range(n+1, m+1):
left = solve_11(seq, n, i, cache) if i > n + 1 else 0
right = solve_10(seq, i, m, cache) if i < m else 0
best = max(best, left + right + seq[n] * seq[i])
cache[key] = best
return best
def solve_11(seq, n, m, cache):
key = ('11', n, m)
if key in cache:
return cache[key]
assert m >= n + 2
if m == n + 2:
return max(seq[n] * seq[n+1] + seq[n+2], seq[n] + seq[n+1] * seq[n+2])
best = -1e9
for i in range(n + 1, m):
left = solve_11(seq, n, i, cache) if i > n + 1 else 0
right = solve_11(seq, i, m, cache) if i < m - 1 else 0
best = max(best, left + right + seq[i] * seq[n] + seq[m], left + right + seq[i] * seq[m] + seq[n])
cache[key] = best
return best
for c in [[1, 1, 1], [4, 2, 3, 5], [1, 2], [1, 2, 3], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]:
print(c, solve_00(c, 0, len(c)-1, dict()))

Confusion Regarding deepest pit within an Array

I got this question as prerequisite for an interview,
A non-empty zero-indexed array A consisting of N integers is given. A
pit in this array is any triplet of integers (P, Q, R) such that: 0 ≤
P < Q < R < N;
sequence [A[P], A[P+1], ..., A[Q]] is strictly decreasing, i.e. A[P] >
A[P+1] > ... > A[Q];
sequence A[Q], A[Q+1], ..., A[R] is strictly increasing, i.e. A[Q] <
A[Q+1] < ... < A[R].
The depth of a pit (P, Q, R) is the number min{A[P] − A[Q], A[R] −
A[Q]}. For example, consider array A consisting of 10 elements such
that:
A[0] = 0
A[1] = 1
A[2] = 3
A[3] = -2
A[4] = 0
A[5] = 1
A[6] = 0
A[7] = -3
A[8] = 2
A[9] = 3
Triplet (2, 3, 4) is one of pits in this array, because sequence
[A[2], A[3]] is strictly decreasing (3 > −2) and sequence [A[3], A[4]]
is strictly increasing (−2 < 0). Its depth is min{A[2] − A[3], A[4] −
A[3]} = 2.
Triplet (2, 3, 5) is another pit with depth 3.
Triplet (5, 7, 8) is yet another pit with depth 4. There is no pit in
this array deeper (i.e. having depth greater) than 4.
It says that Triplet (5, 7, 8) has the deepest pit depth of 4.
but isn't Triplet (2, 7, 9) has the deepest pit depth 6?
corresponding value of Triplet (2, 7, 9) is (3, -3, 3) and it also satisfies the conditions mentioned, i.e.
1) 0 ≤ P < Q < R < N
2) A[P] > A[P+1] > ... > A[Q] and A[Q] < A[Q+1] < ... < A[R]
so in this case min{A[P] − A[Q], A[R] − A[Q]} is 6.
What am i missing here?
P.S. if you think this post does not belong here in this forum then please point out where should i post it.
See the sequence from P to Q for 2 to 7.
It is 3 -2 0 1 0 -3.
sequence [A[P], A[P+1], ..., A[Q]] is strictly decreasing, i.e. A[P] > A[P+1] > ... > A[Q];
The rule says that this should be a decreasing sequence. But it isn't. 3>-2 but -2 is not greater than 0. Here the sequence breaks.
From 7 to 9. No problem as the sequence is increasing. -3<2<3.
answer of the deepest pit problem in swift :
func solution(_ array: [Int]) -> Int {
//guaranty we have at least three elements
if array.isEmpty {
print("isEmpty")
return -1
}
if array.count < 3 {
print("is less than 3")
return -1
}
//extremum point; max or min points
var extremumPoints = [Int]()
//adding first element
extremumPoints.append(array[0])
//calculate extremum points for 1 to one before last element
for i in 1..<(array.count - 1) {
let isRelativeExtremum = ((array[i] - array[i - 1]) * (array[i] - array[i + 1])) > 0
//we call a point semi-extremum if a point is equal to previous element or next element and not equal to previous element or next element
let isSemiExtremum = ((array[i] != array[i - 1]) && (array[i] == array[i + 1])) || ((array[i] != array[i + 1]) && (array[i] == array[i - 1]))
if isRelativeExtremum || isSemiExtremum {
extremumPoints.append(array[i])
}
}
//adding last element
extremumPoints.append(array[array.count - 1])
//we will hold depthes in this array
var depthes = [Int]()
for i in 1..<(extremumPoints.count - 1) {
let isBottomOfaPit = extremumPoints[i] < extremumPoints[i - 1] && extremumPoints[i] < extremumPoints[i + 1]
if isBottomOfaPit {
let d1 = extremumPoints[i - 1] - extremumPoints[i]
let d2 = extremumPoints[i + 1] - extremumPoints[i]
let d = min(d1, d2)
depthes.append(d)
}
}
//deepest pit
let deepestPit = depthes.max()
return deepestPit ?? -1
}
//****************************
let A = [0,1,3,-2,0,1,0,-3,2,3]
let deepestPit = solution(A)
print(deepestPit) // 4
def deepest(A):
def check(p, q, r, A):
if A[p] > A[q] and A[q] < A[r]:
return min(A[p] - A[q], A[r] - A[q])
else:
return -1
max_depth = 0
for i in range(1, len(A) - 2):
if A[i-1] > A[i] < A[i + 1]:
p = i
r = i
while 0 <= p and r <= len(A) - 1:
depth = check(p, i, r, A)
max_depth = max(max_depth, depth)
p -= 1
r += 1
return max_depth

How to find ith item in zigzag ordering?

A question last week defined the zig zag ordering on an n by m matrix and asked how to list the elements in that order.
My question is how to quickly find the ith item in the zigzag ordering? That is, without traversing the matrix (for large n and m that's much too slow).
For example with n=m=8 as in the picture and (x, y) describing (row, column)
f(0) = (0, 0)
f(1) = (0, 1)
f(2) = (1, 0)
f(3) = (2, 0)
f(4) = (1, 1)
...
f(63) = (7, 7)
Specific question: what is the ten billionth (1e10) item in the zigzag ordering of a million by million matrix?
Let's assume that the desired element is located in the upper half of the matrix. The length of the diagonals are 1, 2, 3 ..., n.
Let's find the desired diagonal. It satisfies the following property:
sum(1, 2 ..., k) >= pos but sum(1, 2, ..., k - 1) < pos. The sum of 1, 2, ..., k is k * (k + 1) / 2. So we just need to find the smallest integer k such that k * (k + 1) / 2 >= pos. We can either use a binary search or solve this quadratic inequality explicitly.
When we know the k, we just need to find the pos - (k - 1) * k / 2 element of this diagonal. We know where it starts and where we should move(up or down, depending on the parity of k), so we can find the desired cell using a simple formula.
This solution has an O(1) or an O(log n) time complexity(it depends on whether we use a binary search or solve the inequation explicitly in step 2).
If the desired element is located in the lower half of the matrix, we can solve this problem for a pos' = n * n - pos + 1 and then use symmetry to get the solution to the original problem.
I used 1-based indexing in this solution, using 0-based indexing might require adding +1 or -1 somewhere, but the idea of the solution is the same.
If the matrix is rectangular, not square, we need to consider the fact the length of diagonals look this way: 1, 2, 3, ..., m, m, m, .., m, m - 1, ..., 1(if m <= n) when we search for the k, so the sum becomes something like k * (k + 1) / 2 if k <= m and k * (k + 1) / 2 + m * (k - m) otherwise.
import math, random
def naive(n, m, ord, swap = False):
dx = 1
dy = -1
if swap:
dx, dy = dy, dx
cur = [0, 0]
for i in range(ord):
cur[0] += dy
cur[1] += dx
if cur[0] < 0 or cur[1] < 0 or cur[0] >= n or cur[1] >= m:
dx, dy = dy, dx
if cur[0] >= n:
cur[0] = n - 1
cur[1] += 2
if cur[1] >= m:
cur[1] = m - 1
cur[0] += 2
if cur[0] < 0: cur[0] = 0
if cur[1] < 0: cur[1] = 0
return cur
def fast(n, m, ord, swap = False):
if n < m:
x, y = fast(m, n, ord, not swap)
return [y, x]
alt = n * m - ord - 1
if alt < ord:
x, y = fast(n, m, alt, swap if (n + m) % 2 == 0 else not swap)
return [n - x - 1, m - y - 1]
if ord < (m * (m + 1) / 2):
diag = int((-1 + math.sqrt(1 + 8 * ord)) / 2)
parity = (diag + (0 if swap else 1)) % 2
within = ord - (diag * (diag + 1) / 2)
if parity: return [diag - within, within]
else: return [within, diag - within]
else:
ord -= (m * (m + 1) / 2)
diag = int(ord / m)
within = ord - diag * m
diag += m
parity = (diag + (0 if swap else 1)) % 2
if not parity:
within = m - within - 1
return [diag - within, within]
if __name__ == "__main__":
for i in range(1000):
n = random.randint(3, 100)
m = random.randint(3, 100)
ord = random.randint(0, n * m - 1)
swap = random.randint(0, 99) < 50
na = naive(n, m, ord, swap)
fa = fast(n, m, ord, swap)
assert na == fa, "(%d, %d, %d, %s) ==> (%s), (%s)" % (n, m, ord, swap, na, fa)
print fast(1000000, 1000000, 9999999999, False)
print fast(1000000, 1000000, 10000000000, False)
So the 10-billionth element (the one with ordinal 9999999999), and the 10-billion-first element (the one with ordinal 10^10) are:
[20331, 121089]
[20330, 121090]
An analytical solution
In the general case, your matrix will be divided in 3 areas:
an initial triangle t1
a skewed part mid where diagonals have a constant length
a final triangle t2
Let's call p the index of your diagonal run.
We want to define two functions x(p) and y(p) that give you the column and row of the pth cell.
Initial triangle
Let's look at the initial triangular part t1, where each new diagonal is one unit longer than the preceding.
Now let's call d the index of the diagonal that holds the cell, and
Sp = sum(di) for i in [0..p-1]
We have p = Sp + k, with 0 <=k <= d and
Sp = d(d+1)/2
if we solve for d, it brings
d²+d-2p = 0, a quadratic equation where we retain only the positive root:
d = (-1+sqrt(1+8*p))/2
Now we want the highest integer value closest to d, which is floor(d).
In the end, we have
p = d + k with d = floor((-1+sqrt(1+8*p))/2) and k = p - d(d+1)/2
Let's call
o(d) the function that equals 1 if d is odd and 0 otherwise, and
e(d) the function that equals 1 if d is even and 0 otherwise.
We can compute x(p) and y(p) like so:
d = floor((-1+sqrt(1+8*p))/2)
k = p - d(d+1)/2
o = d % 2
e = 1 - o
x = e*d + (o-e)*k
y = o*d + (e-o)*k
even and odd functions are used to try to salvage some clarity, but you can replace
e(p) with 1 - o(p) and have slightly more efficient but less symetric formulaes for x and y.
Middle part
let's consider the smallest matrix dimension s, i.e. s = min (m,n).
The previous formulaes hold until x or y (whichever comes first) reaches the value s.
The upper bound of p such as x(i) <= s and y(i) <= s for all i in [0..p]
(i.e. the cell indexed by p is inside the initial triangle t1) is given by
pt1 = s(s+1)/2.
For p >= pt1, diagonal length remains equal to s until we reach the second triangle t2.
when inside mid, we have:
p = s(s+1)/2 + ds + k with k in [0..s[.
which yields:
d = floor ((p - s(s+1)/2)/s)
k = p - ds
We can then use the same even/odd trick to compute x(p) and y(p):
p -= s(s+1)/2
d = floor (p / s)
k = p - d*s
o = (d+s) % 2
e = 1 - o
x = o*s + (e-o)*k
y = e*s + (o-e)*k
if (n > m)
x += d+e
y -= e
else
y += d+o
x -= o
Final triangle
Using symetry, we can calculate pt2 = m*n - s(s+1)/2
We now face nearly the same problem as for t1, except that the diagonal may run in the same direction as for t1 or in the reverse direction (if n+m is odd).
Using symetry tricks, we can compute x(p) and y(p) like so:
p = n*m -1 - p
d = floor((-1+sqrt(1+8*p))/2)
k = p - d*(d+1)/2
o = (d+m+n) % 2
e = 1 - $o;
x = n-1 - (o*d + (e-o)*k)
y = m-1 - (e*d + (o-e)*k)
Putting all together
Here is a sample c++ implementation.
I used 64 bits integers out of sheer lazyness. Most could be replaced by 32 bits values.
The computations could be made more effective by precomputing a few more coefficients.
A good part of the code could be factorized, but I doubt it is worth the effort.
Since this is just a quick and dirty proof of concept, I did not optimize it.
#include <cstdio> // printf
#include <algorithm> // min
using namespace std;
typedef long long tCoord;
void panic(const char * msg)
{
printf("PANIC: %s\n", msg);
exit(-1);
}
struct tPoint {
tCoord x, y;
tPoint(tCoord x = 0, tCoord y = 0) : x(x), y(y) {}
tPoint operator+(const tPoint & p) const { return{ x + p.x, y + p.y }; }
bool operator!=(const tPoint & p) const { return x != p.x || y != p.y; }
};
class tMatrix {
tCoord n, m; // dimensions
tCoord s; // smallest dimension
tCoord pt1, pt2; // t1 / mid / t2 limits for p
public:
tMatrix(tCoord n, tCoord m) : n(n), m(m)
{
s = min(n, m);
pt1 = (s*(s + 1)) / 2;
pt2 = n*m - pt1;
}
tPoint diagonal_cell(tCoord p)
{
tCoord x, y;
if (p < pt1) // inside t1
{
tCoord d = (tCoord)floor((-1 + sqrt(1 + 8 * p)) / 2);
tCoord k = p - (d*(d + 1)) / 2;
tCoord o = d % 2;
tCoord e = 1 - o;
x = o*d + (e - o)*k;
y = e*d + (o - e)*k;
}
else if (p < pt2) // inside mid
{
p -= pt1;
tCoord d = (tCoord)floor(p / s);
tCoord k = p - d*s;
tCoord o = (d + s) % 2;
tCoord e = 1 - o;
x = o*s + (e - o)*k;
y = e*s + (o - e)*k;
if (m > n) // vertical matrix
{
x -= o;
y += d + o;
}
else // horizontal matrix
{
x += d + e;
y -= e;
}
}
else // inside t2
{
p = n * m - 1 - p;
tCoord d = (tCoord)floor((-1 + sqrt(1 + 8 * p)) / 2);
tCoord k = p - (d*(d + 1)) / 2;
tCoord o = (d + m + n) % 2;
tCoord e = 1 - o;
x = n - 1 - (o*d + (e - o)*k);
y = m - 1 - (e*d + (o - e)*k);
}
return{ x, y };
}
void check(void)
{
tPoint move[4] = { { 1, 0 }, { -1, 1 }, { 1, -1 }, { 0, 1 } };
tPoint pos;
tCoord dir = 0;
for (tCoord p = 0; p != n * m ; p++)
{
tPoint dc = diagonal_cell(p);
if (pos != dc) panic("zot!");
pos = pos + move[dir];
if (dir == 0)
{
if (pos.y == m - 1) dir = 2;
else dir = 1;
}
else if (dir == 3)
{
if (pos.x == n - 1) dir = 1;
else dir = 2;
}
else if (dir == 1)
{
if (pos.y == m - 1) dir = 0;
else if (pos.x == 0) dir = 3;
}
else
{
if (pos.x == n - 1) dir = 3;
else if (pos.y == 0) dir = 0;
}
}
}
};
void main(void)
{
const tPoint dim[] = { { 10, 10 }, { 11, 11 }, { 10, 30 }, { 30, 10 }, { 10, 31 }, { 31, 10 }, { 11, 31 }, { 31, 11 } };
for (tPoint d : dim)
{
printf("Checking a %lldx%lld matrix...", d.x, d.y);
tMatrix(d.x, d.y).check();
printf("done\n");
}
tCoord p = 10000000000;
tMatrix matrix(1000000, 1000000);
tPoint cell = matrix.diagonal_cell(p);
printf("Coordinates of %lldth cell: (%lld,%lld)\n", p, cell.x, cell.y);
}
Results are checked against "manual" sweep of the matrix.
This "manual" sweep is a ugly hack that won't work for a one-row or one-column matrix, though diagonal_cell() does work on any matrix (the "diagonal" sweep becomes linear in that case).
The coordinates found for the 10.000.000.000th cell of a 1.000.000x1.000.000 matrix seem consistent, since the diagonal d on which the cell stands is about sqrt(2*1e10), approx. 141421, and the sum of cell coordinates is about equal to d (121090+20330 = 141420). Besides, it is also what the two other posters report.
I would say there is a good chance this lump of obfuscated code actually produces an O(1) solution to your problem.

Resources