I want to fast judge two double set intersect or not.
problem
The element in set can be all range. The element in set are not ordered. Each set have 100,000+ element.
If exist a double a from set A, a double b from set B, a and b is very close,for example abs(a-b)<1e-6, we say set A and B intersect.
My way
calculate the range(lower bound and upper bound) of set_A and set_B
O(n), n is set's size
calculate range intersection rang_intersect of range_A and range_B
O(1)
if rang_intersect empty two set not intersect.
O(1)
if range_intersect not empty, find sub_set_A from set_A which in the range_intersect, find sub_set_B from set_B which in the range_intersect
O(n)
sort sub_set_A and sub_set_B
O(mlogm) m is sub_set_A's size
tranvers sub_set_A_sorted and sub_set_B_sorted by two pointer. find if exist element close, if exist two set intersect, if not, two set not intersect.
O(m)
My way can works, but I wonder if I can do faster.
Appendix
Why I want this:
Actually I am face a problem to judge two point set A & B collision or not. Each point p in point set have a double coordinate x,y,z. If exist a point a from point set A, a point b from point set B, a and b's coordinate very close, we say point set A and B collision.
In 3d case, we can define the order of point by first compare x then compare y, last compare z.
We can define the close that if all dimension's coordinate is close , the two point close.
This problem can convert to the problem above.
Some idea by gridding the space:
Let's take the point (1.2, 2.4, 3.6) with minimial distance required 1.
We may say that this point "touches" 8 unit cubes of R^3
[
(1.0, 2.0, 3.5)
(1.0, 2.0, 4.0)
(1.0, 2.5, 3.5) // 1 < 1.2 < 1.5
(1.0, 2.5, 4.0) // 2 < 2.4 < 2.5
(1.5, 2.0, 3.5) // 3.5 < 3.6 < 4
(1.5, 2.0, 4.0)
(1.5, 2.5, 3.5)
(1.5, 2.5, 4.0)
]
If two points are close to each other, their will be connected by some of their cube.
y
^
|
3 +---+---+
| | |
2.5+-------+---+---+
| a | | c | b |
2 +---+---+---+---+--->x
1 1.5 2
In example above in 2D plan, a is (1.2, 2.4).
Say b is (2.5, 2.4). b will touch the square (2,2), but a does not.
So they are not connected (indeed the min distance possible is (2.5-1.5===1).
Say c is (2.45, 2.4). c touches the square (1.5, 2). So is a. We check.
The main idea is to associate to each point its 8 cubes.
We can associate a uniq hash to each cube: the top level coordinate. e.g "{x}-{y}-{z}"
To check if A intersects B:
we build for each point of A its 8 hashes and store them in a hashmap: hash->point
for each point of B, we build the hashes, and if one of those exist in the hashmap we check if the corresponding points are in relation
Now consider
y
^
|
3 +---+---+
| a2| |
2.5+-------+
| a1| |
2 +---+---+
1 1.5 2
a2 and a1 's hashes will overlap on squares (1,2) and (1,2.5). So the hashmap is actually hash->points.
This implies that worst case could be O(n^2) if all the points land into the same cubes. Hopefully in reality they won't?
Below a code with irrelevant data:
(put 10**4 to avoid freezing the ui)
function roundEps (c, nth) {
const eps = 10**-nth
const r = (c % eps)
const v = (r >= eps / 2) ? [c-r+eps/2, c-r+eps] : [c-r, c-r+eps/2]
return v.map(x => x.toFixed(nth + 1))
}
function buildHashes (p, nth) {
return p.reduce((hashes, c) => {
const out = []
hashes.forEach(hash => {
const [l, u] = roundEps(c, nth)
out.push(`${hash},${l}`, `${hash},${u}`)
})
return out
},[''])
}
function buildMap (A, nth) {
const hashToPoints = new Map()
A.forEach(p => {
const hashes = buildHashes(p, nth)
hashes.forEach(hash => {
const v = hashToPoints.get(hash) || []
v.push(p)
hashToPoints.set(hash, v)
})
})
return hashToPoints
}
function intersects (m, b, nth, R) {
let processed = new Set()
return buildHashes(b, nth).some(hash => {
if (!m.has(hash)) return
const pts = m.get(hash)
if (processed.has(pts)) return
processed.add(pts)
return pts.some(p => R(p, b))
})
}
function d (a, b) {
return a.reduce((dist, x, i) => {
return Math.max(dist, Math.abs(x-b[i]))
}, 0)
}
function checkIntersection (A, B, nth=2) {
const m = buildMap(A, nth)
return B.some(b => intersects(m, b, nth, (a,b) => d(a, b) < 10**(-nth)))
}
// ephemeral testing :)
/*
function test () {
const assert = require('assert')
function testRound () {
assert.deepEqual(roundEps(127.857, 2), ['127.855', '127.860'])
assert.deepEqual(roundEps(127.853, 2), ['127.850', '127.855'])
assert.deepEqual(roundEps(127.855, 2), ['127.855', '127.860'])
}
function testD () {
assert.strictEqual(d([1,2,3],[5,1,2]), 4)
assert.strictEqual(d([1,2,3],[0,1,2]), 1)
}
function testCheckIntersection () {
{
const A = [[1.213,2.178,1.254],[0.002,1.231,2.695]]
const B = [[1.213,2.178,1.254],[0.002,1.231,2.695]]
assert(checkIntersection(A, B))
}
{
const A = [[1.213,2.178,1.254],[0.002,1.231,2.695]]
const B = [[10,20,30]]
assert(!checkIntersection(A, B))
}
{
const A = [[0,0,0]]
const B = [[0,0,0.06]]
assert(!checkIntersection(A, B, 2))
}
{
const A = [[0,0,0.013]]
const B = [[0,0,0.006]]
assert(checkIntersection(A, B, 2))
}
}
testRound()
testD()
testCheckIntersection()
}*/
const A = []
const B = []
for (let i = 0; i < 10**4; ++i) {
A.push([Math.random(), Math.random(), Math.random()])
B.push([Math.random(), Math.random(), Math.random()])
}
console.time('start')
console.log('intersect? ', checkIntersection(A, B, 6))
console.timeEnd('start')
Related
Let me describe the picture and then ask the question. So below is the shelf having three rows. Each of the numbers represents a box. The black dot represents the centroid of the box and can be represented in the $(x,y)$ coordinate. This can be seen as input. So input is the list of $(x,y)$ tuples. In this case, there are a list of 29 tuples. I wanted a programme to output the tuples in the order that is
1<< 2 << 3 <<4 ......
My initial idea is to use lexicographic ordering which is defined as follows
(a,b) << (c,d) if and only if a< c or (a= c, b < d)
Its total ordering like that is given any two points we can tell if one is less than the other.
Now here the problem arises, so putting lexicographic ordering I can it can identify
1<< 2 but then as box 3 have a coordinate y less than 2 the order becomes 1<< 3 <<2 and so on.
I put a short code for lexicographic ordering
Data = [(1,1) , (2,1) ,( 1,3), (2,) ]
for i in range(len(data)):
for j in range(i, len(data) - 1):
if data[i][0] > data[j][0]:
temp = data[j]
data[j] = data[i]
data[i] = temp
else:
if data[i][1] > data[j][1]:
temp = data[j]
data[j] = data[i]
data[i] = temp
Now one possible way to get around the problem is to make the boxes stacked on the shelf in horizontal alignment and then use lexicographic ordering. But the point is given input as a list of tuples coordinates it doesn't understand what is adjacent to it. So I am thinking there should be some explanation with graphs. So let's make a graph in which each node is a box and we connect two nodes if the box in the same shelf and adjacent to each other. Now if the node has a degree strictly more than 2 that could be recognised that there is a vertical stacking next to it. Hence now the ordering has to done say only with respect to $y$ ?
Please let me know how can I code this idea or any other package etc deals with a similar idea or reference.
I might have misunderstood something, but if not the following is a working solution for a single shelf.
A shelf of boxes is a set of triples, (x, y, n).
The order relation, <<, on the set is then defined as:
a << b <=> a.x < b.x or a.x = b.x and a.y > b.y
Assumptions:
Two boxes cannot be in the same position, that is, a.x = b.x -> not a.y = b.y
This can be trivially implemented in many programming languages. For example (using a pseudogeneric syntax):
boxes = [ ..., (x, y, n), ... ]
boxes.sort((a,b) =>
if a.x < b.x return a_is_before_b
if a.x = b.x and a.y > b.y return a_is_before_b
return a_is_after_b
)
The above is encoded below in javascript.
function print(lob) {
text = "";
for ( i = 0; i < lob.length; i++ ) {
text += '<br>' + 'Box ' + lob[i][2] + ' at (' + lob[i][0] + ', ' + lob[i][1] + ')'
}
return text;
}
boxes = [ [ 11.5, 3.5, 9 ], [ 9.5, 3.5, 8 ], [ 8.5, 3.5, 7 ], [ 4.5, 1.5, 4 ], [ 7.5, 2.5, 6 ], [ 4.5, 5.5, 2 ], [ 4.5, 3.5, 3 ], [ 7.5, 4.5, 5 ], [ 2.5, 3.5, 1 ] ];
function onload() {
document.getElementById("print1").innerHTML = print(boxes);
boxes.sort( (a, b) => { if ( a[0] < b[0] ) return -1; if ( a[0] == b[0] && a[1] > b[1] ) return -1; return 1 } );
document.getElementById("print2").innerHTML = print(boxes);
}
<body onload="onload()">
List of boxes before sort:
<div id="print1"></div>
<br>
List of boxes after sort:
<div id="print2"></div>
</body>
As I understand, it is related to the partition problem.
But I would like to ask a slightly different problem which I don't care about the sum but the average. In this case, it needs to optimize 2 constraints (sum and number of items) at the same time. It seems to be a harder problem and I cannot see any solutions online.
Are there any solutions for this variant? Or how does it relate to the partition problem?
Example:
input X = [1,1,1,1,1,6]
output based on sum: A = [1,1,1,1,1], B=[6]
output based on average: A = [1], B=[1,1,1,1,6]
On some inputs, a modification of the dynamic program for the usual partition problem will give a speedup. We have to classify each partial solution by its count and sum instead of just sum, which slows things down a bit. Python 3 below (note that the use of dictionaries implicitly collapses functionally identical partial solutions):
def children(ab, x):
a, b = ab
yield a + [x], b
yield a, b + [x]
def proper(ab):
a, b = ab
return a and b
def avg(lst):
return sum(lst) / len(lst)
def abs_diff_avg(ab):
a, b = ab
return abs(avg(a) - avg(b))
def min_abs_diff_avg(lst):
solutions = {(0, 0): ([], [])}
for x in lst:
solutions = {
(sum(a), len(a)): (a, b)
for ab in solutions.values()
for (a, b) in children(ab, x)
}
return min(filter(proper, solutions.values()), key=abs_diff_avg)
print(min_abs_diff_avg([1, 1, 1, 1, 1, 6]))
let S_i the sum of a subset of v of size i
let S be the total sum of v, n the length of v
the err to minimize is
err_i = |avg(S_i) - avg(S-S_i)|
err_i = |S_i/i - (S-S_i)/(n-i)|
err_i = |(nS_i - iS)/(i(n-i))|
algorithm below does:
for all tuple sizes (1,...,n/2) as i
- for all tuples of size i-1 as t_{i-1}
- generate all possible tuple of size i from t_{i-1} by adjoining one elem from v
- track best tuple in regard of err_i
The only cut I found being:
for two tuples of size i having the same sum, keep the one whose last element's index is the smallest
e.g given tuples A, B (where X is some taken element from v)
A: [X,....,X....]
B: [.,X,.....,X..]
keep A because its right-most element has the minimal index
(idea being that at size 3, A will offer the same candidates as B plus some more)
function generateTuples (v, tuples) {
const nextTuples = new Map()
for (const [, t] of tuples) {
for (let l = t.l + 1; l < v.length; ++l) {
const s = t.s + v[l]
if (!nextTuples.has(s) || nextTuples.get(s).l > l) {
const nextTuple = { v: t.v.concat(l), s, l }
nextTuples.set(s, nextTuple)
}
}
}
return nextTuples
}
function processV (v) {
const fErr = (() => {
const n = v.length
const S = v.reduce((s, x) => s + x, 0)
return ({ s: S_i, v }) => {
const i = v.length
return Math.abs((n * S_i - i * S) / (i * (n - i)))
}
})()
let tuples = new Map([[0, { v: [], s: 0, l: -1 }]])
let best = null
let err = 9e3
for (let i = 0; i < Math.ceil(v.length / 2); ++i) {
const nextTuples = generateTuples(v, tuples)
for (const [, t] of nextTuples) {
if (fErr(t) <= err) {
best = t
err = fErr(t)
}
}
tuples = nextTuples
}
const s1Indices = new Set(best.v)
return {
sol: v.reduce(([v1, v2], x, i) => {
(s1Indices.has(i) ? v1 : v2).push(x)
return [v1, v2]
}, [[], []]),
err
}
}
console.log('best: ', processV([1, 1, 1, 1, 1, 6]))
console.log('best: ', processV([1, 2, 3, 4, 5]))
console.log('best: ', processV([1, 3, 5, 7, 7, 8]))
Let's say there is a game where for each move there are probable paths, depending on the throw of fancy dice. Depending on the results there might be transitions forward, backward or staying on one place. Eventually (even after infinite amount of throws) the graph leads to final states. Each edge is weighted with probability .
For the case where there are no loops I can simply sum+multiply and re-normalize the probabilities for each outcome if I start at the same vertex(cell).
However, if I have loops it starts getting confusing. For example, let's say we have same probability on each edge:
start0
/\ ^
/ \ |
end1 tr2
/
end2
the graph starts at start0 and has 50% chance of terminating at end1 or going to transition tr2. From tr2 there is again 50% chance terminating at end2 or going back to start0.
How could I calculate the total probability for reaching each stop end1 and end2. If I try using a converging series like this:
pEnd1=1/2 + 1/2*1/2+1/8+.. ->lim->1. which makes no sense since end2 is getting no probability. Obviously I have a mistake there.
So my question is how can I calculate the probabilities of reaching the final nodes if I have the probabilities of each edge but I could have loops.
example 1) simple fork with a loop all edges are 50% probable
start0-> p=50% ->end1
start0-> p=50% ->tr1
tr2-> p=50% ->start0
tr2-> p=50% ->end2
example 2) more loops
start0-> p=1/3 ->e1
start0-> p=1/3 ->tr1
start0-> p=1/3 ->start0
tr1-> p=1/3 ->tr2
tr1-> p=2/3 ->start0
tr2-> p=7/9 ->start0
tr2-> p=2/9 ->end2
example 3) - degenerate test case - since all paths end at e1 - it should end up having 100%
probability
start0-> p=1/3 ->e1
start0-> p=2/3 ->tr1
tr1-> p=3/4 ->start0
tr2-> p=1/4 ->e1
This is not really a programming problem, although you could write a simulation and perform it 100000 times to see what the distribution would be.
You wrote:
pEnd1=1/2 + 1/2*1/2+1/8+.. ->lim->1. which makes no sense since end2 is getting no probability. Obviously I have a mistake there.
Indeed, there is a mistake. You did not take into account the probability to go from tr2 to start0 (50%). The probability that the path will cycle once to start0 and then end up in end1 is 1/2 (going to tr2) * 1/2 (going back to start0) * 1/2 (going to end1). The number of decisions (of 50%) is always odd when ending up in end1. And it is even when ending up in end2. So the formula would be:
pEnd1 = 2-1 + 2-3 + 2-5 + ... -> lim = 2/3
pEnd2 = 2-2 + 2-4 + 2-6 + ... -> lim = 1/3
Simulation
To make this a programming question, here is a simulation in JavaScript:
function run(transitions, state) {
while (transitions[state][state] != 1) { // not in sink
let probs = transitions[state];
let rnd = Math.random(); // in range [0, 1)
for (let i = 0; i < probs.length; i++) {
rnd -= probs[i];
if (rnd < 0) {
state = i; // transition
break;
}
}
}
return state;
}
// Define graph
let names = ["start0", "end1", "tr2", "end2"]
let transitions = [
[0.0, 0.5, 0.5, 0.0],
[0.0, 1.0, 0.0, 0.0], // sink
[0.5, 0.0, 0.0, 0.5],
[0.0, 0.0, 0.0, 1.0] // sink
];
// Start sampling
let numResults = [0, 0, 0, 0];
let numSamples = 0;
setInterval(function () {
let endstate = run(transitions, 0);
numSamples++;
numResults[endstate]++;
document.querySelector("#" + names[endstate]).textContent = (100 * numResults[endstate] / numSamples).toFixed(4) + "%";
}, 1);
<div>Arriving in end1: <span id="end1"></span></div>
<div>Arriving in end2: <span id="end2"></span></div>
You may also like to read about Absorbing Markov chains. From that we learn that the "absorbing probabilities" matrix B can be calculated with the formula:
B = NR
Where:
N is the "fundamental matrix" (I - Q)⁻¹
I is the identity matrix of the same shape as Q
Q is the probability matrix for transitions between non-final states
R is the probability matrix for transitions to final states
Here is a script (including the relevant matrix functions) to calculate B for the example problem you depicted:
// Probabilities to go from one non-final state to another
let Q = [
[0.0, 0.5], // from start0 to [start0, tr2]
[0.5, 0.0] // from tr2 to [tr2, start0]
];
// Probabilities to go from one non-final state to a final one
let R = [
[0.5, 0.0], // from start0 to [end1, end2]
[0.0, 0.5] // from tr2 to [end1, end2]
];
// See https://en.wikipedia.org/wiki/Absorbing_Markov_chain#Absorbing_probabilities
let N = inversed(sum(identity(Q.length), scalarProduct(Q, -1)));
let B = product(N, R);
console.log("B = (I-Q)⁻¹R:\n" + str(B));
// Generic matrix utility functions:
// cofactor is copy of given matrix without given column and given row
function cofactor(a, y, x) {
return a.slice(0, y).concat(a.slice(y+1)).map(row => row.slice(0, x).concat(row.slice(x+1)));
}
function determinant(a) {
return a.length == 1 ? a[0][0] : a.reduceRight((sum, row, y) =>
a[y][0] * determinant(cofactor(a, y, 0)) - sum
, 0);
}
function adjoint(a) {
return a.length == 1 ? [[1]] : transposed(a.map((row, y) =>
row.map((_, x) => ((x + y) % 2 ? -1 : 1) * determinant(cofactor(a, y, x)))
));
}
function transposed(a) {
return a[0].map((_, x) => a.map((_, y) => a[y][x]));
}
function scalarProduct(a, coeff) {
return a.map((row, y) => row.map((val, x) => val * coeff));
}
function inversed(a) {
return scalarProduct(adjoint(a), 1 / determinant(a));
}
function product(a, b) {
return a.map((rowa) =>
b[0].map((_, x) =>
b.reduce((sum, rowb, z) =>
sum + rowa[z] * rowb[x]
, 0)
)
);
}
function sum(a, b) {
return a.map((row, y) => row.map((val, x) => val + b[y][x]));
}
function identity(length) {
return Array.from({length}, (_, y) =>
Array.from({length}, (_, x) => +(y == x))
);
}
function str(a) {
return a.map(row => JSON.stringify(row)).join("\n");
}
The output is:
[
[2/3, 1/3] // probabilities when starting in start0 and ending in [end1, end2]
[1/3, 2/3] // probabilities when starting in tr2 and ending in [end1, end2]
]
You are describing a discrete-time discrete-state-space Absorbing Markov Chain.
In your example, end 1 and end2 are absorbing states.
The referenced Wikipedia article describes how to calculate absorbing probabilities (or absorption probabilities).
See also here and here.
I am trying to solve the pouring water problem from codechef using scala. The problem statement is as follows:
Given two vessels, one of which can accommodate a liters of water and
the other which can accommodate b liters of water, determine the
number of steps required to obtain exactly c liters of water in one of
the vessels.
At the beginning both vessels are empty. The following operations are
counted as 'steps':
emptying a vessel,
filling a vessel,
pouring water from one vessel to the other, without spilling, until one of the vessels is either full or empty.
Input
An integer t, 1<=t<=100, denoting the number of test cases, followed
by t sets of input data, each consisting of three positive integers a
(the number of liters the first container can hold), b (the number of
liters the second container can hold), and c (the final amount of
liters of water one vessel should contain), not larger than 40000,
given in separate lines.
Output
For each set of input data, output the minimum number of steps
required to obtain c liters, or -1 if this is impossible.
Example Sample input:
2
5
2
3
2
3
4
Sample output:
2
-1
I am approaching this problem as a graph theory problem. Given the initial configuration of containers to be (0, 0), I get the next state of the containers by applying the operations:
FillA, FillB, PourAtoB, PourBtoA, EmptyA, EmptyB recursively until the target is reached.
My code is as follows:
import scala.collection.mutable.Queue
def pour(initA:Int, initB:Int, targetCapacity:Int) {
var pourCombinations = new scala.collection.mutable.HashMap[(Int, Int),Int]
val capacityA = initA
val capacityB = initB
val processingQueue = new Queue[(Int, Int, Int, Int)]
def FillA(a:Int, b:Int) = {
(capacityA, b)
}
def FillB(b:Int, a:Int) = {
(a, capacityB)
}
def PourAtoB(a:Int, b:Int): (Int, Int) = {
if((a == 0) || (b == capacityB)) (a, b)
else PourAtoB(a - 1, b + 1)
}
def PourBtoA(b:Int, a:Int): (Int, Int) = {
if((b == 0) || (a == capacityA)) (a, b)
else PourBtoA(b - 1, a + 1)
}
def EmptyA(a:Int, b:Int) = {
(0, b)
}
def EmptyB(a:Int, b:Int) = {
(a, 0)
}
processingQueue.enqueue((0, 0, targetCapacity, 0))
pourCombinations((0, 0)) = 0
def pourwater(a:Int, b:Int, c:Int, numSteps:Int): Int = {
println(a + ":" + b + ":" + c + ":" + numSteps)
if((a == c) || (b == c)) {return numSteps}
if(processingQueue.isEmpty && (pourCombinations((a,b)) == 1)) {return -1}
//Put all the vals in a List of tuples
val pStateList = scala.List(FillA(a, b), FillB(a, b), PourAtoB(a, b), PourBtoA(b, a), EmptyA(a, b), EmptyB(a, b))
pStateList.foreach{e =>
{
if(!pourCombinations.contains(e)) {
pourCombinations(e) = 0
processingQueue.enqueue((e._1, e._2, c, numSteps + 1))
}
}
}
pourCombinations((a, b)) = 1
val processingTuple = processingQueue.dequeue()
pourwater(processingTuple._1, processingTuple._2, processingTuple._3, processingTuple._4)
}
val intialvalue = processingQueue.dequeue()
pourwater(intialvalue._1, intialvalue._2, intialvalue._3, intialvalue._4)
}
There are a couple of issues with this, first of all I am not sure if I have my base cases of my recursive step set-up properly. Also, it might be that I am not using the proper Scala conventions to solve this problem. Also, I want the pour function to return the numSteps once it is finished executing. It is not doing that at the moment.
It will be great if somebody can go through my code and point out the mistakes with my approach.
Thanks
For example, here is the shape of intended spiral (and each step of the iteration)
y
|
|
16 15 14 13 12
17 4 3 2 11
-- 18 5 0 1 10 --- x
19 6 7 8 9
20 21 22 23 24
|
|
Where the lines are the x and y axes.
Here would be the actual values the algorithm would "return" with each iteration (the coordinates of the points):
[0,0],
[1,0], [1,1], [0,1], [-1,1], [-1,0], [-1,-1], [0,-1], [1,-1],
[2,-1], [2,0], [2,1], [2,2], [1,2], [0,2], [-1,2], [-2,2], [-2,1], [-2,0]..
etc.
I've tried searching, but I'm not exactly sure what to search for exactly, and what searches I've tried have come up with dead ends.
I'm not even sure where to start, other than something messy and inelegant and ad-hoc, like creating/coding a new spiral for each layer.
Can anyone help me get started?
Also, is there a way that can easily switch between clockwise and counter-clockwise (the orientation), and which direction to "start" the spiral from? (the rotation)
Also, is there a way to do this recursively?
My application
I have a sparse grid filled with data points, and I want to add a new data point to the grid, and have it be "as close as possible" to a given other point.
To do that, I'll call grid.find_closest_available_point_to(point), which will iterate over the spiral given above and return the first position that is empty and available.
So first, it'll check point+[0,0] (just for completeness's sake). Then it'll check point+[1,0]. Then it'll check point+[1,1]. Then point+[0,1], etc. And return the first one for which the position in the grid is empty (or not occupied already by a data point).
There is no upper bound to grid size.
There's nothing wrong with direct, "ad-hoc" solution. It can be clean enough too.
Just notice that spiral is built from segments. And you can get next segment from current one rotating it by 90 degrees. And each two rotations, length of segment grows by 1.
edit Illustration, those segments numbered
... 11 10
7 7 7 7 6 10
8 3 3 2 6 10
8 4 . 1 6 10
8 4 5 5 5 10
8 9 9 9 9 9
// (di, dj) is a vector - direction in which we move right now
int di = 1;
int dj = 0;
// length of current segment
int segment_length = 1;
// current position (i, j) and how much of current segment we passed
int i = 0;
int j = 0;
int segment_passed = 0;
for (int k = 0; k < NUMBER_OF_POINTS; ++k) {
// make a step, add 'direction' vector (di, dj) to current position (i, j)
i += di;
j += dj;
++segment_passed;
System.out.println(i + " " + j);
if (segment_passed == segment_length) {
// done with current segment
segment_passed = 0;
// 'rotate' directions
int buffer = di;
di = -dj;
dj = buffer;
// increase segment length if necessary
if (dj == 0) {
++segment_length;
}
}
}
To change original direction, look at original values of di and dj. To switch rotation to clockwise, see how those values are modified.
Here's a stab at it in C++, a stateful iterator.
class SpiralOut{
protected:
unsigned layer;
unsigned leg;
public:
int x, y; //read these as output from next, do not modify.
SpiralOut():layer(1),leg(0),x(0),y(0){}
void goNext(){
switch(leg){
case 0: ++x; if(x == layer) ++leg; break;
case 1: ++y; if(y == layer) ++leg; break;
case 2: --x; if(-x == layer) ++leg; break;
case 3: --y; if(-y == layer){ leg = 0; ++layer; } break;
}
}
};
Should be about as efficient as it gets.
This is the javascript solution based on the answer at
Looping in a spiral
var x = 0,
y = 0,
delta = [0, -1],
// spiral width
width = 6,
// spiral height
height = 6;
for (i = Math.pow(Math.max(width, height), 2); i>0; i--) {
if ((-width/2 < x && x <= width/2)
&& (-height/2 < y && y <= height/2)) {
console.debug('POINT', x, y);
}
if (x === y
|| (x < 0 && x === -y)
|| (x > 0 && x === 1-y)){
// change direction
delta = [-delta[1], delta[0]]
}
x += delta[0];
y += delta[1];
}
fiddle: http://jsfiddle.net/N9gEC/18/
This problem is best understood by analyzing how changes coordinates of spiral corners. Consider this table of first 8 spiral corners (excluding origin):
x,y | dx,dy | k-th corner | N | Sign |
___________________________________________
1,0 | 1,0 | 1 | 1 | +
1,1 | 0,1 | 2 | 1 | +
-1,1 | -2,0 | 3 | 2 | -
-1,-1 | 0,-2 | 4 | 2 | -
2,-1 | 3,0 | 5 | 3 | +
2,2 | 0,3 | 6 | 3 | +
-2,2 | -4,0 | 7 | 4 | -
-2,-2 | 0,-4 | 8 | 4 | -
By looking at this table we can calculate X,Y of k-th corner given X,Y of (k-1) corner:
N = INT((1+k)/2)
Sign = | +1 when N is Odd
| -1 when N is Even
[dx,dy] = | [N*Sign,0] when k is Odd
| [0,N*Sign] when k is Even
[X(k),Y(k)] = [X(k-1)+dx,Y(k-1)+dy]
Now when you know coordinates of k and k+1 spiral corner you can get all data points in between k and k+1 by simply adding 1 or -1 to x or y of last point.
Thats it.
good luck.
I would solve it using some math. Here is Ruby code (with input and output):
(0..($*.pop.to_i)).each do |i|
j = Math.sqrt(i).round
k = (j ** 2 - i).abs - j
p = [k, -k].map {|l| (l + j ** 2 - i - (j % 2)) * 0.5 * (-1) ** j}.map(&:to_i)
puts "p => #{p[0]}, #{p[1]}"
end
E.g.
$ ruby spiral.rb 10
p => 0, 0
p => 1, 0
p => 1, 1
p => 0, 1
p => -1, 1
p => -1, 0
p => -1, -1
p => 0, -1
p => 1, -1
p => 2, -1
p => 2, 0
And golfed version:
p (0..$*.pop.to_i).map{|i|j=Math.sqrt(i).round;k=(j**2-i).abs-j;[k,-k].map{|l|(l+j**2-i-j%2)*0.5*(-1)**j}.map(&:to_i)}
Edit
First try to approach the problem functionally. What do you need to know, at each step, to get to the next step?
Focus on plane's first diagonal x = y. k tells you how many steps you must take before touching it: negative values mean you have to move abs(k) steps vertically, while positive mean you have to move k steps horizontally.
Now focus on the length of the segment you're currently in (spiral's vertices - when the inclination of segments change - are considered as part of the "next" segment). It's 0 the first time, then 1 for the next two segments (= 2 points), then 2 for the next two segments (= 4 points), etc. It changes every two segments and each time the number of points part of that segments increase. That's what j is used for.
Accidentally, this can be used for getting another bit of information: (-1)**j is just a shorthand to "1 if you're decreasing some coordinate to get to this step; -1 if you're increasing" (Note that only one coordinate is changed at each step). Same holds for j%2, just replace 1 with 0 and -1 with 1 in this case. This mean they swap between two values: one for segments "heading" up or right and one for those going down or left.
This is a familiar reasoning, if you're used to functional programming: the rest is just a little bit of simple math.
It can be done in a fairly straightforward way using recursion. We just need some basic 2D vector math and tools for generating and mapping over (possibly infinite) sequences:
// 2D vectors
const add = ([x0, y0]) => ([x1, y1]) => [x0 + x1, y0 + y1];
const rotate = θ => ([x, y]) => [
Math.round(x * Math.cos(θ) - y * Math.sin(θ)),
Math.round(x * Math.sin(θ) + y * Math.cos(θ))
];
// Iterables
const fromGen = g => ({ [Symbol.iterator]: g });
const range = n => [...Array(n).keys()];
const map = f => it =>
fromGen(function*() {
for (const v of it) {
yield f(v);
}
});
And now we can express a spiral recursively by generating a flat line, plus a rotated (flat line, plus a rotated (flat line, plus a rotated ...)):
const spiralOut = i => {
const n = Math.floor(i / 2) + 1;
const leg = range(n).map(x => [x, 0]);
const transform = p => add([n, 0])(rotate(Math.PI / 2)(p));
return fromGen(function*() {
yield* leg;
yield* map(transform)(spiralOut(i + 1));
});
};
Which produces an infinite list of the coordinates you're interested in. Here's a sample of the contents:
const take = n => it =>
fromGen(function*() {
for (let v of it) {
if (--n < 0) break;
yield v;
}
});
const points = [...take(5)(spiralOut(0))];
console.log(points);
// => [[0,0],[1,0],[1,1],[0,1],[-1,1]]
You can also negate the rotation angle to go in the other direction, or play around with the transform and leg length to get more complex shapes.
For example, the same technique works for inward spirals as well. It's just a slightly different transform, and a slightly different scheme for changing the length of the leg:
const empty = [];
const append = it1 => it2 =>
fromGen(function*() {
yield* it1;
yield* it2;
});
const spiralIn = ([w, h]) => {
const leg = range(w).map(x => [x, 0]);
const transform = p => add([w - 1, 1])(rotate(Math.PI / 2)(p));
return w * h === 0
? empty
: append(leg)(
fromGen(function*() {
yield* map(transform)(spiralIn([h - 1, w]));
})
);
};
Which produces (this spiral is finite, so we don't need to take some arbitrary number):
const points = [...spiralIn([3, 3])];
console.log(points);
// => [[0,0],[1,0],[2,0],[2,1],[2,2],[1,2],[0,2],[0,1],[1,1]]
Here's the whole thing together as a live snippet if you want play around with it:
// 2D vectors
const add = ([x0, y0]) => ([x1, y1]) => [x0 + x1, y0 + y1];
const rotate = θ => ([x, y]) => [
Math.round(x * Math.cos(θ) - y * Math.sin(θ)),
Math.round(x * Math.sin(θ) + y * Math.cos(θ))
];
// Iterables
const fromGen = g => ({ [Symbol.iterator]: g });
const range = n => [...Array(n).keys()];
const map = f => it =>
fromGen(function*() {
for (const v of it) {
yield f(v);
}
});
const take = n => it =>
fromGen(function*() {
for (let v of it) {
if (--n < 0) break;
yield v;
}
});
const empty = [];
const append = it1 => it2 =>
fromGen(function*() {
yield* it1;
yield* it2;
});
// Outward spiral
const spiralOut = i => {
const n = Math.floor(i / 2) + 1;
const leg = range(n).map(x => [x, 0]);
const transform = p => add([n, 0])(rotate(Math.PI / 2)(p));
return fromGen(function*() {
yield* leg;
yield* map(transform)(spiralOut(i + 1));
});
};
// Test
{
const points = [...take(5)(spiralOut(0))];
console.log(JSON.stringify(points));
}
// Inward spiral
const spiralIn = ([w, h]) => {
const leg = range(w).map(x => [x, 0]);
const transform = p => add([w - 1, 1])(rotate(Math.PI / 2)(p));
return w * h === 0
? empty
: append(leg)(
fromGen(function*() {
yield* map(transform)(spiralIn([h - 1, w]));
})
);
};
// Test
{
const points = [...spiralIn([3, 3])];
console.log(JSON.stringify(points));
}
Here is a Python implementation based on the answer by #mako.
def spiral_iterator(iteration_limit=999):
x = 0
y = 0
layer = 1
leg = 0
iteration = 0
yield 0, 0
while iteration < iteration_limit:
iteration += 1
if leg == 0:
x += 1
if (x == layer):
leg += 1
elif leg == 1:
y += 1
if (y == layer):
leg += 1
elif leg == 2:
x -= 1
if -x == layer:
leg += 1
elif leg == 3:
y -= 1
if -y == layer:
leg = 0
layer += 1
yield x, y
Running this code:
for x, y in spiral_iterator(10):
print(x, y)
Yields:
0 0
1 0
1 1
0 1
-1 1
-1 0
-1 -1
0 -1
1 -1
2 -1
2 0
Try searching for either parametric or polar equations. Both are suitable to plotting spirally things. Here's a page that has plenty of examples, with pictures (and equations). It should give you some more ideas of what to look for.
I've done pretty much the same thin as a training exercise, with some differences in the output and the spiral orientation, and with an extra requirement, that the functions spatial complexity has to be O(1).
After think for a while I came to the idea that by knowing where does the spiral start and the position I was calculating the value for, I could simplify the problem by subtracting all the complete "circles" of the spiral, and then just calculate a simpler value.
Here is my implementation of that algorithm in ruby:
def print_spiral(n)
(0...n).each do |y|
(0...n).each do |x|
printf("%02d ", get_value(x, y, n))
end
print "\n"
end
end
def distance_to_border(x, y, n)
[x, y, n - 1 - x, n - 1 - y].min
end
def get_value(x, y, n)
dist = distance_to_border(x, y, n)
initial = n * n - 1
(0...dist).each do |i|
initial -= 2 * (n - 2 * i) + 2 * (n - 2 * i - 2)
end
x -= dist
y -= dist
n -= dist * 2
if y == 0 then
initial - x # If we are in the upper row
elsif y == n - 1 then
initial - n - (n - 2) - ((n - 1) - x) # If we are in the lower row
elsif x == n - 1 then
initial - n - y + 1# If we are in the right column
else
initial - 2 * n - (n - 2) - ((n - 1) - y - 1) # If we are in the left column
end
end
print_spiral 5
This is not exactly the thing you asked for, but I believe it'll help you to think your problem
I had a similar problem, but I didn't want to loop over the entire spiral each time to find the next new coordinate. The requirement is that you know your last coordinate.
Here is what I came up with with a lot of reading up on the other solutions:
function getNextCoord(coord) {
// required info
var x = coord.x,
y = coord.y,
level = Math.max(Math.abs(x), Math.abs(y));
delta = {x:0, y:0};
// calculate current direction (start up)
if (-x === level)
delta.y = 1; // going up
else if (y === level)
delta.x = 1; // going right
else if (x === level)
delta.y = -1; // going down
else if (-y === level)
delta.x = -1; // going left
// check if we need to turn down or left
if (x > 0 && (x === y || x === -y)) {
// change direction (clockwise)
delta = {x: delta.y,
y: -delta.x};
}
// move to next coordinate
x += delta.x;
y += delta.y;
return {x: x,
y: y};
}
coord = {x: 0, y: 0}
for (i = 0; i < 40; i++) {
console.log('['+ coord.x +', ' + coord.y + ']');
coord = getNextCoord(coord);
}
Still not sure if it is the most elegant solution. Perhaps some elegant maths could remove some of the if statements. Some limitations would be needing some modification to change spiral direction, doesn't take into account non-square spirals and can't spiral around a fixed coordinate.
I have an algorithm in java that outputs a similar output to yours, except that it prioritizes the number on the right, then the number on the left.
public static String[] rationals(int amount){
String[] numberList=new String[amount];
int currentNumberLeft=0;
int newNumberLeft=0;
int currentNumberRight=0;
int newNumberRight=0;
int state=1;
numberList[0]="("+newNumberLeft+","+newNumberRight+")";
boolean direction=false;
for(int count=1;count<amount;count++){
if(direction==true&&newNumberLeft==state){direction=false;state=(state<=0?(-state)+1:-state);}
else if(direction==false&&newNumberRight==state){direction=true;}
if(direction){newNumberLeft=currentNumberLeft+sign(state);}else{newNumberRight=currentNumberRight+sign(state);}
currentNumberLeft=newNumberLeft;
currentNumberRight=newNumberRight;
numberList[count]="("+newNumberLeft+","+newNumberRight+")";
}
return numberList;
}
Here's the algorithm. It rotates clockwise, but could easily rotate anticlockwise, with a few alterations. I made it in just under an hour.
// spiral_get_value(x,y);
sx = argument0;
sy = argument1;
a = max(sqrt(sqr(sx)),sqrt(sqr(sy)));
c = -b;
d = (b*2)+1;
us = (sy==c and sx !=c);
rs = (sx==b and sy !=c);
bs = (sy==b and sx !=b);
ls = (sx==c and sy !=b);
ra = rs*((b)*2);
ba = bs*((b)*4);
la = ls*((b)*6);
ax = (us*sx)+(bs*-sx);
ay = (rs*sy)+(ls*-sy);
add = ra+ba+la+ax+ay;
value = add+sqr(d-2)+b;
return(value);`
It will handle any x / y values (infinite).
It's written in GML (Game Maker Language), but the actual logic is sound in any programming language.
The single line algorithm only has 2 variables (sx and sy) for the x and y inputs. I basically expanded brackets, a lot. It makes it easier for you to paste it into notepad and change 'sx' for your x argument / variable name and 'sy' to your y argument / variable name.
`// spiral_get_value(x,y);
sx = argument0;
sy = argument1;
value = ((((sx==max(sqrt(sqr(sx)),sqrt(sqr(sy))) and sy !=(-1*max(sqrt(sqr(sx)),sqrt(sqr(sy))))))*((max(sqrt(sqr(sx)),sqrt(sqr(sy))))*2))+(((sy==max(sqrt(sqr(sx)),sqrt(sqr(sy))) and sx !=max(sqrt(sqr(sx)),sqrt(sqr(sy)))))*((max(sqrt(sqr(sx)),sqrt(sqr(sy))))*4))+(((sx==(-1*max(sqrt(sqr(sx)),sqrt(sqr(sy)))) and sy !=max(sqrt(sqr(sx)),sqrt(sqr(sy)))))*((max(sqrt(sqr(sx)),sqrt(sqr(sy))))*6))+((((sy==(-1*max(sqrt(sqr(sx)),sqrt(sqr(sy)))) and sx !=(-1*max(sqrt(sqr(sx)),sqrt(sqr(sy))))))*sx)+(((sy==max(sqrt(sqr(sx)),sqrt(sqr(sy))) and sx !=max(sqrt(sqr(sx)),sqrt(sqr(sy)))))*-sx))+(((sx==max(sqrt(sqr(sx)),sqrt(sqr(sy))) and sy !=(-1*max(sqrt(sqr(sx)),sqrt(sqr(sy))))))*sy)+(((sx==(-1*max(sqrt(sqr(sx)),sqrt(sqr(sy)))) and sy !=max(sqrt(sqr(sx)),sqrt(sqr(sy)))))*-sy))+sqr(((max(sqrt(sqr(sx)),sqrt(sqr(sy)))*2)+1)-2)+max(sqrt(sqr(sx)),sqrt(sqr(sy)));
return(value);`
I know the reply is awfully late :D but i hope it helps future visitors.