Collect puzzle from curve fragments - curve-fitting

I have a data, which consists of a number of chunks. I now that they come from some continuous curve, but later were shifted in the y-direction. Now I want to shift them back to estimate original curve. Some parts are not shifted, but just absent. To clarify the situation dummy code to generate something similar is below (Matlab):
%% generate some dummy data
knots = rand(10,2);
% fix starting and stop points
knots = [[0,rand()];knots;[1,rand()]];
% sort knots
knots=unique(knots,'rows');
% generate dummy curve
dummyX = linspace(0,1,10^4);
dummyY = interp1(knots(:,1),knots(:,2),dummyX,'spline');
figure()
subplot(2,1,1)
plot(dummyX,dummyY)
%% Add offset and wipe some parts
% get borders of chunks
borders = [1;randi([1,numel(dummyX)],20,1);numel(dummyX)];
borders = unique(borders);
borders = [borders(1:end-1)+1,borders(2:end)];
borders(1) = 1;
% add ofsets or nans
offset = (rand(size(borders,1),1)-0.5)*5;
offset(randperm(numel(offset),floor(size(borders,1)/3)))=nan;
for iBorder = 1:size(borders,1)
idx = borders(iBorder,1): borders(iBorder,2);
dummyY(idx)=dummyY(idx)+offset(iBorder);
dummyY(idx([1,end]))=nan;
end
subplot(2,1,2)
plot(dummyX,dummyY)
Original curve is on top, shifted on the bottom. I try to shift chunks pairwise, minimizing the length of the cubic spline, but it did not work for me. I understand, that it is impossible to obtain absolutely same curve (I may lose some peaks).
Could You help me to find the best shifts?

I had several ideas for this and played with overall curvature, arc length, etc. as well as mixed combinations. Turned out that a simple chi**2 works best. So it goes as simple as this:
Get some knots to fit every chunk with a given precision by splines
join everything
reduce knots to avoid very close knots in touching sets, those can result in large curvature.
use leastsq fit on entire set with splines on joined and reduced set of knots to find shifts.
In theory one could play with / modify:
spline order
min knot density
max knot density
how adjacent sets are dealt with
adding a knot to a large gap
etc.
(Note: In some random data the splrev produced error messages. As those are mostly not very helpful, I can only say that this code is not 100% robust.)
Code is as follows
import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import interp1d, splrep, splev
from scipy.optimize import fmin, leastsq
def reduce_knots( inList, dist ):
outList=[]
addList=[]
for i in inList:
try:
if abs( i - addList[ -1 ] ) < dist:
addList += [ i ]
else:
outList += [ addList ]
addList = [ i ]
except IndexError:### basically the first
addList = [ i]
outList += [ addList ]
return [ sum( x ) / len( x ) for x in outList ]
def adaptive_knots( inX, inY, thresh=.005 ):
ll = len( inX )
sup = ll - 4
assert sup > 3
nN = 3
test = True
while test:
testknots = np.linspace( 1, len( inX ) - 2, nN, dtype=np.int )
testknots = [ inX[ x ] for x in testknots ]
myTCK= splrep( inX , inY, t=testknots )
newY = splev( inX , myTCK )
chi2 = np.sum( ( newY - inY )**2 ) / ll
if chi2 > thresh:
nN += 1
if nN > sup:
test = False
else:
test = False
return testknots
def global_residuals( shiftList, xBlocks, yBlocks, allTheKnots ):# everything shifted (1 is redundant by global offset) Blocks must be ordered an np.arrays
localYBlocks = [ s + yList for s, yList in zip( shiftList, yBlocks ) ]
allTheX = np.concatenate( xBlocks )
allTheY = np.concatenate( localYBlocks )
tck = splrep( allTheX, allTheY, t=allTheKnots )
yList = splev( allTheX, tck )
diff = yList - allTheY
return diff
#~ np.random.seed( 28561 )
np.random.seed( 5561 )
#~ np.random.seed( 733437 )
### python way for test data
knots = np.random.rand( 8, 2 )
knots = np.array( sorted( [ [ 0, np.random.rand() ] ] + list( knots ) + [ [ 1, np.random.rand() ] ], key=lambda x: x[ 0 ] ) )
dummyX = np.linspace( 0, 1, 3e4 )
f = interp1d( knots[ :, 0 ], knots[ :, 1 ], 'cubic' )
dummyY = np.fromiter( ( f( x ) for x in dummyX ), np.float )
chunk = np.append( [ 0 ], np.append( np.sort( np.random.randint( 7, high=len( dummyX ) - 10 , size= 10, dtype=np.int ) ), len( dummyX ) ) )
xDataDict = dict()
yDataDict = dict()
allX = np.array( [] )
allY = np.array( [] )
allK = np.array( [] )
allS = []
for i, val in enumerate(chunk[ : -1 ] ):
if np.random.rand() < .75: ## 25% of not appearing
xDataDict[ i ] = dummyX[ val:chunk[ i + 1 ] ]
realShift = 1.5 * ( 1 - 2 * np.random.rand() )
allS += [ realShift ]
yDataDict[ i ] = dummyY[ val:chunk[ i + 1 ] ] + realShift
yDataDict[ i ] = np.fromiter( ( np.random.normal( scale=.05, loc=y ) for y in yDataDict[ i ] ), np.float )
allX = np.append( allX, xDataDict[ i ] )
allY = np.append( allY, yDataDict[ i ] )
### Plotting
fig = plt.figure()
ax = fig.add_subplot( 3, 1, 1 )
ax.plot( knots[ :, 0 ],knots[ :, 1 ], ls='', c='r', marker='o')
ax.plot( dummyX , dummyY, '--' )
for key in xDataDict.keys():
ax.plot(xDataDict[ key ], yDataDict[ key ] )
myKnots = adaptive_knots( xDataDict[ key ], yDataDict[ key ] )
allK = np.append( allK, myKnots )
myTCK = splrep( xDataDict[ key ], yDataDict[ key ], t=myKnots )
ax.plot( xDataDict[ key ], splev( xDataDict[ key ] , myTCK ) )
myTCK = splrep( allX, allY, t=allK )
ax.plot( allX, splev( allX, myTCK ) )
for x in allK:
ax.axvline( x=x, linestyle=':', color='#AAAAAA', linewidth=1 )
### now fitting
myXBlockList = []
myYBlockList = []
for key in sorted( xDataDict.keys() ):
myXBlockList += [ xDataDict[ key ] ]
myYBlockList += [ yDataDict[ key ] ]
#start values
s = [ 0 ]
for i,y in enumerate( myYBlockList[ :-1 ] ):
ds = myYBlockList[ i + 1 ][ 0 ] - y[ -1 ]
s += [ -ds ]
startShift = np.cumsum( s )
allK = reduce_knots( allK, .01 )
sol, ierr = leastsq( global_residuals, x0=startShift, args=( myXBlockList, myYBlockList, allK ), maxfev=10000 )
sol = np.array(sol) - sol[ 0 ]
print "solution: ", -sol
print "real: ", np.array( allS ) - allS[ 0 ]
### Plotting solutions
bx = fig.add_subplot( 3, 1, 3, sharex=ax )
for x, y, s in zip( myXBlockList, myYBlockList, sol ):
bx.plot( x, y + s )
localYBlocks = [ s + yList for s,yList in zip( sol, myYBlockList ) ]
allTheX = np.concatenate( myXBlockList )
allTheY = np.concatenate( localYBlocks )
tck = splrep( allTheX, allTheY, t=allK )
dx = allTheX[ 1 ] - allTheX[ 0 ]
testX = np.arange( allTheX[ 0 ], allTheX[ -1 ], dx )
finalyList = splev( testX, tck)
bx.plot( testX, finalyList , 'k--' )
mean = sum( dummyY ) / len( dummyY ) - sum( finalyList ) / len( finalyList )
bx.plot( dummyX, dummyY - mean, '--' )
for x in allK:
bx.axvline( x=x, linestyle=':', color='#AAAAAA', linewidth=1 )
cx = fig.add_subplot( 3, 1, 2, sharex=ax )
for x, y, s in zip( myXBlockList, myYBlockList, startShift ):
cx.plot( x, y + s )
plt.show()
For small gaps this works nicely on the test data
The upper graph shows the original spline and its knots as red dots. This generated the data. Moreover, it shows the noisy shifted chunks, the initial fitting knots as vertical lines and an according spline fit.
Mid graph shows the chunks shifted by the pre-calculated start values - aligned ends.
Lower graph shows original spline, fitted spline, reduced knot positions, and chunks shifted according to the fit solution.
Naturally, the larger the gaps the more the solution deviates from the original
...but still quite good.

Related

How to fit response plot using square wave

I'm looking for a way/method to fit my response data (Image is shown below). So using f(t) = (square(2*pi*f*t)+1) to filter my raw data. However, cftool don't recognize this kind of function. So please help me thanks!
The function below might allow to fit the data. It is continuous, but not differentiable everywhere. The steps tend to fall to the right, while OPs data does not. This might require some extra work. Moreover, steps have to be equidistant, which, however, seems to be the case.
# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import numpy as np
def f( x, a, b ): # test function (that would be the one to fit, actually + a shift of edge position)
return a + b * x**3
def f_step( x, l, func, args=None ):
y = ( x - l / 2. ) % l - l / 2.
y = y / l * 2.
p = np.floor( ( x-l/2.) / (l) ) + 1
centre = p * l
left = centre - l / 2.
right = centre + l / 2.
fL = func( left, *args )
fR = func( right, *args )
fC = func( centre, *args )
out = fC + sharp( y , fL - fC, fR - fC , 5 )
return out
def sharp( x, a, b , p, epsilon=1e-1 ):
out = a * ( 1. / abs( x + 1 + epsilon )**p - ( 2 + epsilon)**( -p ) ) / ( epsilon**( -p ) - ( 2 + epsilon )**( -p ) )
out += b * ( 1. /abs( x - 1 - epsilon )**p - ( 2 + epsilon)**( -p ) ) / ( epsilon**( -p ) - ( 2 + epsilon )**( -p ) )
return out
l=0.57
xList = np.linspace( -1, 1.75, 500 )
yList = [ f_step( x, l, f, args=(2, -.3 ) ) for x in xList ]
fig1 = plt.figure( 1 )
ax = fig1.add_subplot( 1, 1, 1 )
ax.plot( xList, yList )
ax.plot( xList, f(xList, 2,-.3) )
plt.show()
Looks like:

What limits the size of a Python module?

I am compiling into Python 3(.4.4) and have generated a program that is 250,000 lines long. When I tried running it, Python crashed: Windows (10) reported "python.exe has stopped working". Shorter versions of the "same" output run OK so I'm wondering if the problem is that my program is too long and if so, how I can increase the limit?
Please note that I am not interested in "solutions" where my output is factored into smaller chunks. A monolithic output file is the part of the problem specification.
Here is what the program looks like:
import os, sys
from random import randint, seed
from datetime import datetime
DEAD = '_'
ALIVE = '1'
cells = [] # Will be an array of max_row+2 rows each of max_col+2 columns.
# Create initial population of cells
seed( 1.3 )
def repeat_run( max_run ):
print( '%20s %20s %20s' % ( 'Time', 'Rate', 'Density' ) )
for run in range( max_run ):
blank_row = [ DEAD for col in range( 152 ) ]
for row in range( 152 ):
cells.append( blank_row.copy() )
pop = 0
for row in range( 1, 152-1 ):
for col in range( 1, 152-1 ):
if randint( 0, 1 ) == 0:
cells[ row ][ col ] = ALIVE
pop += 1
time, rate, density = simulate( cells, pop )
print( '%20.5f %20.5f %20.5f' % ( time, rate, density ) )
print()
def num_neighb( row, col ):
count = 0
for col_inc in range( -1, 2 ):
x = col + col_inc
for row_inc in range( -1, 2 ):
y = row + row_inc
if cells[ y ][ x ] == ALIVE:
count += 1
return count
def simulate( cells, pop ):
# Global tally of all cells that ever lived (for calculating average
# density over the entire run).
grand_total = pop
start = datetime.now()
for gen in range( 10 ):
pop = 0 # Number of live cells in next generation
# Initialise next generation of cells
next_gen = [ [ DEAD for col in range( 152 ) ] for col in range( 152 ) ]
# Apply birth/death rules
nn = num_neighb( 1, 1 )
if cells[ 1 ][ 1 ] == DEAD:
if nn == 3:
next_gen[ 1 ][ 1 ] = ALIVE
pop += 1
else:
if nn == 3 or nn == 4:
next_gen[ 1 ][ 1 ] = ALIVE
pop += 1
# 250,000 lines later ...
nn = num_neighb( 150, 150 )
if cells[ 150 ][ 150 ] == DEAD:
if nn == 3:
next_gen[ 150 ][ 150 ] = ALIVE
pop += 1
else:
if nn == 3 or nn == 4:
next_gen[ 150 ][ 150 ] = ALIVE
pop += 1
grand_total += pop
# Copy next_gen to cells
for col in range( 152 ):
for row in range( 152 ):
cells[ row ][ col ] = next_gen[ row ][ col ]
end = datetime.now()
delta = ( end - start ).total_seconds()
return delta, 231040 / delta, grand_total / 231040
repeat_run( 10 )
The full program is available here.
Thanks for your thoughts.
Mailing list claims:
So you can have 600KB of source code or 350KB of pyc without any
trouble whatsoever.
At the same time, number of locals (and thus function arguments) is capped. Likewise indentation depth is capped too. Limits are 255 and 100 respectively, and you'd get a nice error if you exceed those.

Reversing this spiral function [duplicate]

I'm using Alberto Santini's solution to this question to get a spiral grid reference based on an items index
Algorithm for iterating over an outward spiral on a discrete 2D grid from the origin
It's not the accepted solution, but it's the best for my needs as it avoids using a loop.
It's working well, but what I want now is to do the inverse. Based on a known x and y coordinate return the index of a location.
This is as a precursor to returning the items surrounding a given location.
Pascal code:
if y * y >= x * x then begin
p := 4 * y * y - y - x;
if y < x then
p := p - 2 * (y - x)
end
else begin
p := 4 * x * x - y - x;
if y < x then
p := p + 2 *(y - x)
end;
Description: Left-upper semi-diagonal (0-4-16-36-64) contains squared layer number (4 * layer^2). External if-statement defines layer and finds (pre-)result for position in corresponding row or column of left-upper semi-plane, and internal if-statement corrects result for mirror position.
I don't know if there is a concise mathematical equation to derive what you want, but I have a solution that computes what you want in O(1) time per query. No loops like you wanted.
My approach :
(i) For any given point (x,y), find the number of points which lie in the square of side length (2*a-1), where a = Max( |x|, |y| ). These are the interior points. i.e, the number of points lying in all spirals NOT including current spiral.
This is nothing but ( 2*a -1 )*( 2*a -1 )
Eg : Consider the following diagram :
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
|
|
For the point ( 2,1 ), a = 2. The interior points, here are labelled as 0, 1, 2, 3, 4, 5, 6, 7, 8 - The square with edge length 3
(ii) Now compute the points lying on the current spiral. The spiral has 4 "corner" points -
(a) The starting point ( where the current spiral starts )
(b) The point ( a, a )
(c) The point ( -a, a )
(d) The point ( -a, -a )
So, I compute the number of elements lying between each such pair [ i.e, between (a) and (b), (b) and (c), (c) and (d) ], such that all of these fall before the required input point in the spiral sequence. This can be done by simple subtraction of point co-ordinates.
This value, plus the number of interior points will give you the required answer.
I am not sure whether I have explained this very clearly. Do let me know if you require any clarifications or further explanation.
Attached is the JAVA code I wrote to test my logic. I am sorry but it is not very elegant, but it works :P
import java.io.IOException;
import java.util.Scanner;
class Pnt
{
int x, y;
public Pnt( int _x, int _y )
{
x = _x;
y = _y;
}
}
public class Spiral
{
static int interior( Pnt p ) // returns points within interior square of side length MAX( x, y ) - 1
{
int a = Math.max( Math.abs( p.x ), Math.abs( p.y ));
return ( 2*a - 1 )*( 2*a - 1 );
}
static Pnt startPnt( Pnt p ) // first point in that spiral
{
int a = Math.max( Math.abs( p.x ), Math.abs( p.y ));
// last pnt in prev spiral = [ a-1, -( a-1 ) ]
// next pnt = [ a, -( a-1 ) ]
return new Pnt( a, -( a-1 ));
}
static int offSetRow1( Pnt pStart, Pnt p )
{
return ( p.y - pStart.y ) + 1;
}
static int solve( Pnt curr )
{
// check location of curr
// It may lie on 1st row, 2nd row, 3rd or 4th row
int a = Math.max( Math.abs( curr.x ), Math.abs( curr.y ));
int off=0;
int interiorCnt = interior( curr );
Pnt start = startPnt( curr );
if( ( curr.x == a ) && ( curr.y >= start.y ) ) // row 1
{
off = offSetRow1( start, curr );
return off+interiorCnt;
}
if( curr.y == a ) // row 2
{
Pnt start2 = new Pnt( a, a );
int off1 = offSetRow1( start, start2 );
// now add diff in x-coordinates
int off2 = start2.x - curr.x;
off = off1 + off2;
return off+interiorCnt;
}
if( curr.x == -a ) // row 3
{
Pnt start2 = new Pnt( a, a );
int off1 = offSetRow1( start, start2 );
// now add diff in x-coordinates
Pnt start3 = new Pnt( -a, a );
int off2 = start2.x - start3.x;
// now add diff in y co-ordinates
int off3 = start3.y - curr.y;
off = off1 + off2 + off3;
return off+interiorCnt;
}
else // row 4
{
Pnt start2 = new Pnt( a, a );
int off1 = offSetRow1( start, start2 );
// now add diff in x-coordinates
Pnt start3 = new Pnt( -a, a );
int off2 = start2.x - start3.x;
// now add diff in y co-ordinates
int off3 = start3.y - curr.y;
Pnt start4 = new Pnt( -a, -a );
// add diff in x co-ordinates
int off4 = curr.x - start4.x;
off = off1 + off2 + off3 + off4;
return interiorCnt + off;
}
}
public static void main( String[] args ) throws IOException
{
Scanner s = new Scanner( System.in );
while( true )
{
int x = s.nextInt();
int y = s.nextInt();
Pnt curr = new Pnt( x, y );
System.out.println( solve( curr ));
}
}
}
I want to throw in my function since it's a bit more concise than the last solution but more complex than the first.
rather than have the indexes adjacent to each-other, my code opts for loops/layers where the first index of the next loop is always on the same axis.
like so:
23 24 9 10 11 +y
22 8 1 2 12
21 7 0 3 13
20 6 5 4 14
19 18 17 16 15 -y
-x +x
it has set directions and uses the smaller vec2 value as the offset from these NSEW axes
func translate_vector2_to_spiral_index(vec2):
#layer is the ring level the position is on
var layer = max(abs(vec2.x),abs(vec2.y))
if layer == 0:
return 0
#the total interior positions before the edge
var base_index = 0
var i = 0
while i < layer:
base_index += 8 * i
i+=1
var current_layer_total = 8 * i
#non_axis spaces at each corner (not directly any nesw axis)
var non_axis_spaces = (current_layer_total - 4)/4
#direct axes spaces on this layer
var N = 1
var E = N + non_axis_spaces + 1
var S = E + non_axis_spaces + 1
var W = S + non_axis_spaces + 1
var spiral_index = base_index
if abs(vec2.x) > abs(vec2.y):
if vec2.x < 0:
spiral_index+=W
spiral_index += vec2.y
elif vec2.x > 0:
spiral_index+=E
spiral_index -= vec2.y
else:
if vec2.y < 0:
spiral_index+=S
elif vec2.y > 0:
spiral_index+=N
#abs(y) must be equivalent to layers if x is 0
else:
if vec2.y < 0:
spiral_index+=S
spiral_index -= vec2.x
elif vec2.y > 0:
spiral_index
var x = N
x += vec2.x
#if x goes into the negative on the iteration axis (N) it's a subtraction from the layer total
if vec2.x < 0:
x = current_layer_total + 1 + vec2.x
spiral_index += x
else:
if vec2.x < 0:
spiral_index+=W
elif vec2.x > 0:
spiral_index+=E
#abs(x) must be equivalent to layers if y is 0
return spiral_index
there's probably a way to shorten this but i thought to throw this out there.

Is there a way to make this code faster and if possible avoid loops?

A1, B1, C1, A2, B2 and C2 are 6 matrix with the same dimensions 4435X2000.
I have to find the values i, j and k for which A1(k,2000) == A2(i,j) and B1(k,2000) == B2(i,j) and C1(k,2000) == C2(i,j) , with the condition X(k)==1 and Y(i,j)==1
The objective is to find: counter, L, T and D
Is there a way to make this code faster? Can I avoid loops?
counter=0;
L(1)=0;
T(1)=0;
D(1)=0;
for k=1:4435
if X(k)==1 % X is a vector (4435x1)
F(k,:) = [A1(k,2000) B1(k,2000) C1(k,2000)]
for i=1:4435
for j=100:1999
if Y(i,j)==1 % Y is a matrix (4435x1999)
if F(k,:) == [A2(i,j) B2(i,j) C2(i,j)]
counter = counter+1;
L(counter)=k;
T(counter)=i;
D(counter)=j;
end
end
end
end
end
end
I want a solution that will save me at least 80% of the computation time!
and not have the error message: Out of memory
See how this works out for you -
%// Store X-Y data by calling X() and Y() functions
X_data = X(1:4435);
Y_data = Y(1:4435,100:1999);
range1 = 100:1999 %// define range for columns
A2 = A2(:,range1); %// Crop out A2, B2, C2 based on column-range
B2 = B2(:,range1);
C2 = C2(:,range1);
Y_data = Y_data(:,range1)==1;
%// Indices for dim-3
idx_X = find(X_data==1)
%// Map X==1 onto A1, B1, C1
A1Lr = A1(X_data==1,end)
B1Lr = B1(X_data==1,end)
C1Lr = C1(X_data==1,end)
%// Setup output array to store L, T, D as single Nx3 output array
out = zeros(sum(Y_data(:))*numel(A1Lr),3);
%// Try out(sum(Y_data(:)==1)*numel(A1Lr),3)=0; instead for speed!
%// Start collecting output indices
count = 1;
for iter1 = 1:numel(A1Lr)
[R,C] = find(Y_data & A2==A1Lr(iter1) & B2==B1Lr(iter1) & C2==C1Lr(iter1));
nR = numel(R);
out(count:count+nR-1,:) = [R C repmat(iter1,nR,1)];
count = count + nR;
end
out(find(out(:,1)==0,1):end,:)=[];
%// Packup the outputs
T = out(:,1)
D = out(:,2) + range1(1)-1
L = idx_X(out(:,3))
It is very difficult to determine what your code is actually supposed to accomplish, without really working to interpret your code. However, I'll give it a crack:
% Determine where X is true.
XTrue = X == 1;
% Extract values from A1,B1,C1 where X is true.
F ( XTrue , 1 : 3 ) = [ A1(XTrue,2000) B1(XTrue,2000) C1(XTrue,2000) ];
% Determine where Y is true.
YTrueIndex = find ( Y == 1 );
% Determine where the extracted values match
counter = [];
L = [];
T = [];
D = [];
for ( ii = 1 : length(YTrueIndex) )
indexCurrent = YTrueIndex(ii)
FRowsThatMatch = F(:,1)==A2(indexCurrent) & F(:,2)==B2(indexCurrent) & F(:,3)==C2(indexCurrent);
matchCount = length ( find ( FRowsThatMatch ) );
if ( matchCount > 0 )
counter = counter + matchCount;
[ i , j ] = ind2sub ( size ( Y ) , indexCurrent );
L = [ L , find ( FRowsThatMatch ) ];
T = [ T , ones(matchCount,1)*i ];
D = [ D , ones(matchCount,2)*j ];
end
end

Get spiral index from location

I'm using Alberto Santini's solution to this question to get a spiral grid reference based on an items index
Algorithm for iterating over an outward spiral on a discrete 2D grid from the origin
It's not the accepted solution, but it's the best for my needs as it avoids using a loop.
It's working well, but what I want now is to do the inverse. Based on a known x and y coordinate return the index of a location.
This is as a precursor to returning the items surrounding a given location.
Pascal code:
if y * y >= x * x then begin
p := 4 * y * y - y - x;
if y < x then
p := p - 2 * (y - x)
end
else begin
p := 4 * x * x - y - x;
if y < x then
p := p + 2 *(y - x)
end;
Description: Left-upper semi-diagonal (0-4-16-36-64) contains squared layer number (4 * layer^2). External if-statement defines layer and finds (pre-)result for position in corresponding row or column of left-upper semi-plane, and internal if-statement corrects result for mirror position.
I don't know if there is a concise mathematical equation to derive what you want, but I have a solution that computes what you want in O(1) time per query. No loops like you wanted.
My approach :
(i) For any given point (x,y), find the number of points which lie in the square of side length (2*a-1), where a = Max( |x|, |y| ). These are the interior points. i.e, the number of points lying in all spirals NOT including current spiral.
This is nothing but ( 2*a -1 )*( 2*a -1 )
Eg : Consider the following diagram :
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
|
|
For the point ( 2,1 ), a = 2. The interior points, here are labelled as 0, 1, 2, 3, 4, 5, 6, 7, 8 - The square with edge length 3
(ii) Now compute the points lying on the current spiral. The spiral has 4 "corner" points -
(a) The starting point ( where the current spiral starts )
(b) The point ( a, a )
(c) The point ( -a, a )
(d) The point ( -a, -a )
So, I compute the number of elements lying between each such pair [ i.e, between (a) and (b), (b) and (c), (c) and (d) ], such that all of these fall before the required input point in the spiral sequence. This can be done by simple subtraction of point co-ordinates.
This value, plus the number of interior points will give you the required answer.
I am not sure whether I have explained this very clearly. Do let me know if you require any clarifications or further explanation.
Attached is the JAVA code I wrote to test my logic. I am sorry but it is not very elegant, but it works :P
import java.io.IOException;
import java.util.Scanner;
class Pnt
{
int x, y;
public Pnt( int _x, int _y )
{
x = _x;
y = _y;
}
}
public class Spiral
{
static int interior( Pnt p ) // returns points within interior square of side length MAX( x, y ) - 1
{
int a = Math.max( Math.abs( p.x ), Math.abs( p.y ));
return ( 2*a - 1 )*( 2*a - 1 );
}
static Pnt startPnt( Pnt p ) // first point in that spiral
{
int a = Math.max( Math.abs( p.x ), Math.abs( p.y ));
// last pnt in prev spiral = [ a-1, -( a-1 ) ]
// next pnt = [ a, -( a-1 ) ]
return new Pnt( a, -( a-1 ));
}
static int offSetRow1( Pnt pStart, Pnt p )
{
return ( p.y - pStart.y ) + 1;
}
static int solve( Pnt curr )
{
// check location of curr
// It may lie on 1st row, 2nd row, 3rd or 4th row
int a = Math.max( Math.abs( curr.x ), Math.abs( curr.y ));
int off=0;
int interiorCnt = interior( curr );
Pnt start = startPnt( curr );
if( ( curr.x == a ) && ( curr.y >= start.y ) ) // row 1
{
off = offSetRow1( start, curr );
return off+interiorCnt;
}
if( curr.y == a ) // row 2
{
Pnt start2 = new Pnt( a, a );
int off1 = offSetRow1( start, start2 );
// now add diff in x-coordinates
int off2 = start2.x - curr.x;
off = off1 + off2;
return off+interiorCnt;
}
if( curr.x == -a ) // row 3
{
Pnt start2 = new Pnt( a, a );
int off1 = offSetRow1( start, start2 );
// now add diff in x-coordinates
Pnt start3 = new Pnt( -a, a );
int off2 = start2.x - start3.x;
// now add diff in y co-ordinates
int off3 = start3.y - curr.y;
off = off1 + off2 + off3;
return off+interiorCnt;
}
else // row 4
{
Pnt start2 = new Pnt( a, a );
int off1 = offSetRow1( start, start2 );
// now add diff in x-coordinates
Pnt start3 = new Pnt( -a, a );
int off2 = start2.x - start3.x;
// now add diff in y co-ordinates
int off3 = start3.y - curr.y;
Pnt start4 = new Pnt( -a, -a );
// add diff in x co-ordinates
int off4 = curr.x - start4.x;
off = off1 + off2 + off3 + off4;
return interiorCnt + off;
}
}
public static void main( String[] args ) throws IOException
{
Scanner s = new Scanner( System.in );
while( true )
{
int x = s.nextInt();
int y = s.nextInt();
Pnt curr = new Pnt( x, y );
System.out.println( solve( curr ));
}
}
}
I want to throw in my function since it's a bit more concise than the last solution but more complex than the first.
rather than have the indexes adjacent to each-other, my code opts for loops/layers where the first index of the next loop is always on the same axis.
like so:
23 24 9 10 11 +y
22 8 1 2 12
21 7 0 3 13
20 6 5 4 14
19 18 17 16 15 -y
-x +x
it has set directions and uses the smaller vec2 value as the offset from these NSEW axes
func translate_vector2_to_spiral_index(vec2):
#layer is the ring level the position is on
var layer = max(abs(vec2.x),abs(vec2.y))
if layer == 0:
return 0
#the total interior positions before the edge
var base_index = 0
var i = 0
while i < layer:
base_index += 8 * i
i+=1
var current_layer_total = 8 * i
#non_axis spaces at each corner (not directly any nesw axis)
var non_axis_spaces = (current_layer_total - 4)/4
#direct axes spaces on this layer
var N = 1
var E = N + non_axis_spaces + 1
var S = E + non_axis_spaces + 1
var W = S + non_axis_spaces + 1
var spiral_index = base_index
if abs(vec2.x) > abs(vec2.y):
if vec2.x < 0:
spiral_index+=W
spiral_index += vec2.y
elif vec2.x > 0:
spiral_index+=E
spiral_index -= vec2.y
else:
if vec2.y < 0:
spiral_index+=S
elif vec2.y > 0:
spiral_index+=N
#abs(y) must be equivalent to layers if x is 0
else:
if vec2.y < 0:
spiral_index+=S
spiral_index -= vec2.x
elif vec2.y > 0:
spiral_index
var x = N
x += vec2.x
#if x goes into the negative on the iteration axis (N) it's a subtraction from the layer total
if vec2.x < 0:
x = current_layer_total + 1 + vec2.x
spiral_index += x
else:
if vec2.x < 0:
spiral_index+=W
elif vec2.x > 0:
spiral_index+=E
#abs(x) must be equivalent to layers if y is 0
return spiral_index
there's probably a way to shorten this but i thought to throw this out there.

Resources