How to add elliptic curve points in python? - python

I'm trying to implement a simple elliptic curve encryption program but I can't get the expected output of doubling and adding a Point P till 12P .The curve equation isy^2 = x^3 +ax + b mod p. According to this site 3P = [10, 6] when P = [5, 1] while I get 3p = [10, 5]. The equations I use can be found on Wikipedia.
P = [5, 1]
prime = 17
a = 2
b = 2
def gcdExtended(a, b):
if a == 0:
return b, 0, 1
gcd, x1, y1 = gcdExtended(b % a, a)
x = y1 - (b // a) * x1
y = x1
return gcd, x, y
def double_point(point: list):
x = point[0]
y = point[1]
s = ((3*(x**2)+a) * (gcdExtended(2*y, prime)[1])) % prime
newx = (s**2 - x - x) % prime
newy = (s * (x - newx) - y) % prime
return [newx, newy]
def add_points(P: list, Q: list):
x1 = P[0]
y1 = P[1]
x2 = Q[0]
y2 = Q[1]
s = ((y2 - y1) * ((gcdExtended(x2-x1, prime))[1] % prime)) % prime
newx = (s**2 - x1 - x2) % prime
newy = (s * (x1 - newx) - y1) % prime
return [newx, newy]
Q = P
index = 2
while True:
if Q[0] == P[0] and Q[1] == P[1]:
print("doubling")
Q = double_point(P)
else:
print("adding")
Q = add_points(Q, P)
if index == 12 :
break
print(f"{index}P = {Q}")
index += 1

If the point [5,1] is added successively, the following sequence is obtained:
1P = [ 5, 1]
2P = [ 6, 3]
3P = [10, 6]
4P = [ 3, 1]
5P = [ 9, 16]
6P = [16, 13]
7P = [ 0, 6]
8P = [13, 7]
9P = [ 7, 6]
10P = [ 7, 11]
11P = [13, 10]
12P = [ 0, 11]
13P = [16, 4]
14P = [ 9, 1]
15P = [ 3, 16]
16P = [10, 11]
17P = [ 6, 14]
18P = [ 5, 16]
19P = point at infinity
This can be verified e.g. here.
The problem in the posted code is that the method to determine the modular inverse, gcdExtended(a, b), is only valid for positive a and b. While in double_point and add_points b has the value prime (= 17 > 0), a can take negative values.
gcdExtended generally returns wrong values for negative a:
The modular inverse of 5 or -12 is 7: 5 x 7 mod17 = 35 mod17 = 1 and 7 x (-12) mod17 = -84 mod17 = 85 mod17 = 1.
The gcdExtended returns for these values: gcdExtended(5, 17)[1] = 7 (which is true) and gcdExtended(-12, 17)[1] = -7 (which is false).
To allow negative values for a, e.g. the following methods can be defined, see here:
def sign(x):
return 1 if x >= 0 else -1
def gcdExtendedGeneralized(a, b):
gcd, x1, y1 = gcdExtended(abs(a), b)
return gcd, (sign(a) * x1) % b, y1 % b
Replacing gcdExtended with gcdExtendedGeneralized in double_point and add_points provides the correct values (note that the current implementation does not consider the point at infinity).

You interchanged P and Q in add_points. Also a small simplification in your calculation of s:
def add_points(P: list, Q: list):
x1 = P[0]
y1 = P[1]
x2 = Q[0]
y2 = Q[1]
#s = ((y2 - y1) * ((gcdExtended(x2-x1, prime))[1] % prime)) % prime
s = (y2-y1) * (gcdExtended(x2-x1, prime)[1] % prime)
newx = (s**2 - x1 - x2) % prime
newy = (s * (x1 - newx) - y1) % prime
return [newx, newy]
Q = P
index = 2
while True:
if Q[0] == P[0] and Q[1] == P[1]:
print("doubling")
Q = double_point(P)
else:
print("adding")
Q = add_points(P, Q)
if index == 12 :
break
print(f"{index}P = {Q}")
index += 1
which results in
doubling
2P = [6, 3]
adding
3P = [10, 6]
adding
4P = [3, 1]
adding
5P = [9, 16]
adding
6P = [16, 13]
adding
7P = [0, 6]
adding
8P = [13, 8]
adding
9P = [8, 7]
adding
10P = [8, 10]
adding
11P = [13, 9]
adding

Related

Operations on Sequences - Codewars Kata - Best Performance

This is a codewars problem. The idea is given that arr = [x(1), x(2), x(3), x(4), ..., x(i), x(i+1), ..., x(m), x(m+1)] where all x(i) are positive integers. (the length (len(arr)) of this array will be a positive multiple of 4)
And let P = (x(1) ** 2 + x(2) ** 2) * (x(3) ** 2 + x(4) ** 2) * ... * (x(m) ** 2 + x(m+1) ** 2)
Find A and B such that A**2 + B**2 == P
If only one solution is found, it is already valid.
I solved, but the efficiency is low. Initially I wrote this:
[Original Method]
from math import sqrt, ceil
def solve(arr):
if len(arr) % 2 != 0: return []
n = len(arr)
P = 1
i = 0
while i < n:
P *= (arr[i]**2 + arr[i + 1]**2)
i += 2
m = ceil(sqrt(P))
for A in range(1, m):
B = sqrt(P - A**2)
if B == int(B):
return [A, int(B)]
return []
To improve the performance I tried to use the Cauchy inequality.
[Improvement but Failing]
from math import sqrt, ceil
def solve(arr):
if len(arr) % 4 != 0: return []
n = len(arr)
P = 1
for i in range(0, n, 2):
P *= (arr[i]**2 + arr[i + 1]**2)
C = 1
D = 1
for i in range(0, n, 4):
C *= (arr[i]**2 + arr[i + 1]**2) * (arr[i + 2]**2 + arr[i + 3]**2)
D *= (arr[i] * arr[i + 2] + arr[i + 1] * arr[i + 3])**2
A = int(sqrt(D))
B = int(sqrt(P - A**2))
if A ** 2 + B ** 2 == P:
return [A, B]
return []
But the code above starts having problems for high numbers. For example: arr = [3, 9, 8, 4, 6, 8, 7, 8, 4, 8, 5, 6, 6, 4, 4, 5] should result in A = 13243200 and B = 25905600, but my result is that not has A and B for this case.
I need help to improve my performance. I don't know if using the Cauchy inequality solves it, but if you have another idea it's already valid.

I kept getting "ModuleNotFoundError: No module named 'error'"

I was given an outline of this code from my teacher, however I kept getting ModuleNotFoundError: No module named 'error'. I know that I need a module named error, however when I couldn't find a code for a module named error.
I am trying to solve this question:
Solve the tridiagonal equations Ax = b by Doolittle’s decomposition method, where:
A = [6 2 0 0 0
−1 7 2 0 0
0 −2 8 2 0
0 0 3 7 −2
0 0 0 3 5]
b = [2
−3
4
−3
1].
Here is the code that I was using:
from numpy import argmax, dot, zeros, array, asarray, tril, triu
def swapRows(v,i,j):
if len(v.shape) == 1: v[i],v[j] = v[j],v[i]
else:
temp = v[i].copy()
v[i] = v[j]
v[j] = temp
def swapCols(v,i,j):
temp = v[:,j].copy()
v[:,j] = v[:,i]
v[:,i] = temp
import error
def LUdecomp(a,tol=1.0e-9):
n = len(a)
seq = array(range(n))
# Set up scale factors
s = zeros(n)
for i in range(n):
s[i] = max(abs(a[i,:]))
for k in range(0,n-1):
# Row interchange, if needed
p = argmax(abs(a[k:n,k])/s[k:n]) + k
if abs(a[p,k]) < tol: error.err('Matrix is singular')
if p != k:
swapRows(s,k,p)
swapRows(a,k,p)
swapRows(seq,k,p)
# Elimination
for i in range(k+1,n):
if a[i,k] != 0.0:
lam = a[i,k]/a[k,k]
a[i,k+1:n] = a[i,k+1:n] - lam*a[k,k+1:n]
a[i,k] = lam
return a,seq
def LUsolve(a,b,seq):
n = len(a)
# Rearrange constant vector; store it in [x]
x = b.copy()
for i in range(n):
x[i] = b[seq[i]]
# Solution
for k in range(1,n):
x[k] = x[k] - dot(a[k,0:k],x[0:k])
x[n-1] = x[n-1]/a[n-1,n-1]
for k in range(n-2,-1,-1):
x[k] = (x[k] - dot(a[k,k+1:n],x[k+1:n]))/a[k,k]
return x
A = asarray( [ [ 6, 2, 0, 0, 0 ],
[ -1, 7, 2, 0, 0 ],
[ 0, -2, 8, 2, 0 ],
[ 0, 0, 3, 7, -2 ],
[ 0, 0, 0, 3, 5 ] ], dtype=float ) A_orig = A.copy() b = asarray( [ 2, -3, 4, -3, 1 ], dtype=float ) b_orig = b.copy()
A,seq = LUdecomp(A) # A is overwritten as L\U L = tril( A, -1 ) # extract L for ii in range(L.shape[0]): L[ii,ii] = 1.0 # add in 1's on the diagonal U = triu( A, 0 ) # extract U print ("L = ") print (L) print ("U = ") print (U) if False:
print ("A[seq,:]= ")
print (A_orig[seq,:])
print ("LU= ")
print (dot(L,U))
x = LUsolve(A,b,seq) print ("Solution= ", x)
If your intention is to throw an error at some point, then you can reach that without the import error statement. Raising an exception might be a solution.
See the documentation on errors and exceptions.
You can remove import error and edit
if abs(a[p,k]) < tol: error.err('Matrix is singular')
in LUdecomp() as follows:
if abs(a[p,k]) < tol:
raise Exception('Matrix is singular')

How to loop through lists from pandas dataframe in a function

Here is my dataframe,
df = pd.DataFrame({'Id': [102,103,104,303,305],'ExpG_Home':[1.8,1.5,1.6,1.8,2.9],
'ExpG_Away':[2.2,1.3,1.2,2.8,0.8],
'HomeG_Time':[[93, 109, 187],[169], [31, 159],[176],[16, 48, 66, 128]],
'AwayG_Time':[[90, 177],[],[],[123,136],[40]]})
First, I need to create an array y, for a given Id number, it takes values from same row (ExpG_Home & ExpG_Away).
y = [1 - (ExpG_Home + ExpG_Away), ExpG_Home, ExpG_Away]
Second, I found this much harder, for the Id used in creating y, the function below takes the corresponding lists from HomeG_Time & AwayG_Time and creates an array. Unfortunately, my function takes one row at a time. I need to do this for a large dataset.
x1 = [1,0,0]
x2 = [0,1,0]
x3 = [0,0,1]
total_timeslot = 200 # number of timeslot per game.
k = 1 # constant
#For Id=102 with ExpG_Home=2.2 and ExpG_Away=1.8
HomeG_Time = [93, 109, 187]
AwayG_Time = [90, 177]
y = np.array([1-(2.2 + 1.8)/k, 2.2/k, 1.8/k])
# output of y = [0.98 , 0.011, 0.009]
def squared_diff(x1, x2, x3, y):
ssd = []
for k in range(total_timeslot):
if k in HomeG_Time:
ssd.append(sum((x2 - y) ** 2))
elif k in AwayG_Time:
ssd.append(sum((x3 - y) ** 2))
else:
ssd.append(sum((x1 - y) ** 2))
return ssd
sum(squared_diff(x1, x2, x3, y))
Out[37]: 7.880400000000012
This output is for the first row only.
Here is the complete snippet given,
>>> import numpy as np
>>> x1 = np.array( [1,0,0] )
>>> x2 = np.array( [0,1,0] )
>>> x3 = np.array( [0,0,1] )
>>> total_timeslot = 200
>>> HomeG_Time = [93, 109, 187]
>>> AwayG_Time = [90, 177]
>>> ExpG_Home=2.2
>>> ExpG_Away=1.8
>>> y = np.array( [1 - (ExpG_Home + ExpG_Away), ExpG_Home, ExpG_Away] )
>>> def squared_diff(x1, x2, x3, y):
... ssd = []
... for k in range(total_timeslot):
... if k in HomeG_Time:
... ssd.append(sum((x2 - y) ** 2))
... elif k in AwayG_Time:
... ssd.append(sum((x3 - y) ** 2))
... else:
... ssd.append(sum((x1 - y) ** 2))
... return ssd
...
>>> sum(squared_diff(x1, x2, x3, y))
4765.599999999989
Assuming this. Calculate y as (N,3) using pandas.DataFrame.apply
>>> y = np.array( df.apply(lambda row: [1 - (row.ExpG_Home + row.ExpG_Away),
... row.ExpG_Home, row.ExpG_Away ],
... axis=1).tolist() )
>>> y.shape
(5, 3)
Now calcualte squared error for a given x
>>> def squared_diff(x, y):
... return np.sum( np.square(x - y), axis=1)
In your case, if error2 is squared_diff(x2,y) you are adding this the number of occuerences of HomeG_Time
>>> n3 = df.AwayG_Time.apply(len)
>>> n2 = df.HomeG_Time.apply(len)
>>> n1 = 200 - (n2 + n3)
The final sum of squared error is (as per your calculation)
>>> squared_diff(x1, y) * n1 + squared_diff(x2, y) * n2 + squared_diff(x3, y) * n3
0 4766.4
1 2349.4
2 2354.4
3 6411.6
4 4496.2
dtype: float64
>>>
try this,
import pandas as pd
import numpy as np
df = pd.DataFrame({'Id': [102,103,104,303,305],'ExpG_Home':[1.8,1.5,1.6,1.8,2.9],
'ExpG_Away':[2.2,1.3,1.2,2.8,0.8],
'HomeG_Time':[[93, 109, 187],[169], [31, 159],[176],[16, 48, 66, 128]],
'AwayG_Time':[[90, 177],[],[],[123,136],[40]]})
x1 = [1,0,0]
x2 = [0,1,0]
x3 = [0,0,1]
k=1
total_timeslot = 200 # number of timeslot per game.
def squared_diff(x1, x2, x3,AwayG_Time,HomeG_Time, y):
ssd = []
for k in range(total_timeslot):
if k in HomeG_Time:
ssd.append(sum((x2 - y) ** 2))
elif k in AwayG_Time:
ssd.append(sum((x3 - y) ** 2))
else:
ssd.append(sum((x1 - y) ** 2))
return ssd
s=pd.DataFrame( pd.concat([df,1-(df['ExpG_Home']+df['ExpG_Away'])/k,df['ExpG_Home']/k,df['ExpG_Away']/k],axis=1).values)
df['res']=s.apply(lambda x: sum(squared_diff(x1,x2,x3,x[0],x[3],np.array([x[5],x[6],x[7]]))),axis=1)
del s
print df
Output:
AwayG_Time ExpG_Away ExpG_Home HomeG_Time Id res
0 [90, 177] 2.2 1.8 [93, 109, 187] 102 4766.4
1 [] 1.3 1.5 [169] 103 2349.4
2 [] 1.2 1.6 [31, 159] 104 2354.4
3 [123, 136] 2.8 1.8 [176] 303 6411.6
4 [40] 0.8 2.9 [16, 48, 66, 128] 305 4496.2
def squared_diff(row):
y = np.array([1 - (row.ExpG_Home + row.ExpG_Away), row.ExpG_Home, row.ExpG_Away])
HomeG_Time = row.HomeG_Time
AwayG_Time = row.AwayG_Time
x1 = np.array([1, 0, 0])
x2 = np.array([0, 1, 0])
x3 = np.array([0, 0, 1])
total_timeslot = 200
ssd = []
for k in range(total_timeslot):
if k in HomeG_Time:
ssd.append(sum((x2 - y) ** 2))
elif k in AwayG_Time:
ssd.append(sum((x3 - y) ** 2))
else:
ssd.append(sum((x1 - y) ** 2))
return sum(ssd)
df.apply(squared_diff, axis=1)
Out[]:
0 4766.4
1 2349.4
2 2354.4
3 6411.6
4 4496.2

creating a spiral array in python?

Me and my mate were trying to create a fun game in python where the elements entered in the array are accessed in a spiral manner. I have tried few methods like one given below (source).
def spiral(X, Y):
x = y = 0
dx = 0
dy = -1
for i in range(max(X, Y)**2):
if (-X/2 < x <= X/2) and (-Y/2 < y <= Y/2):
print (x, y)
# DO STUFF...
if x == y or (x < 0 and x == -y) or (x > 0 and x == 1-y):
dx, dy = -dy, dx
x, y = x+dx, y+dy
The above statement accesses the elements in spiral loop and prints them for a defined array AE. I would like to know how can I transform a given array AE to a spiral one
You can build a spiral by starting near the center of the matrix and always turning right unless the element has been visited already:
#!/usr/bin/env python
NORTH, S, W, E = (0, -1), (0, 1), (-1, 0), (1, 0) # directions
turn_right = {NORTH: E, E: S, S: W, W: NORTH} # old -> new direction
def spiral(width, height):
if width < 1 or height < 1:
raise ValueError
x, y = width // 2, height // 2 # start near the center
dx, dy = NORTH # initial direction
matrix = [[None] * width for _ in range(height)]
count = 0
while True:
count += 1
matrix[y][x] = count # visit
# try to turn right
new_dx, new_dy = turn_right[dx,dy]
new_x, new_y = x + new_dx, y + new_dy
if (0 <= new_x < width and 0 <= new_y < height and
matrix[new_y][new_x] is None): # can turn right
x, y = new_x, new_y
dx, dy = new_dx, new_dy
else: # try to move straight
x, y = x + dx, y + dy
if not (0 <= x < width and 0 <= y < height):
return matrix # nowhere to go
def print_matrix(matrix):
width = len(str(max(el for row in matrix for el in row if el is not None)))
fmt = "{:0%dd}" % width
for row in matrix:
print(" ".join("_"*width if el is None else fmt.format(el) for el in row))
Example:
>>> print_matrix(spiral(5, 5))
21 22 23 24 25
20 07 08 09 10
19 06 01 02 11
18 05 04 03 12
17 16 15 14 13
Introductory remarks
The question is closely related to a problem of printing an array in spiral order. In fact, if we already have a function which does it, then the problem in question is relatively simple.
There is a multitude of resources on how to produce a spiral matrix or how to loop or print an array in spiral order. Even so, I decided to write my own version, using numpy arrays. The idea is not original but use of numpy makes the code more concise.
The other reason is that most of examples of producing a spiral matrix I found (including the code in the question and in the other answers) deal only with square matrices of size n x n for odd n. Finding the start (or end) point in matrices of other sizes may be tricky. For example, for a 3x5 matrix it can't be the middle cell. The code below is general and the position of the starting (ending) point depends on the choice of the function spiral_xxx.
Code
The first function unwraps an array in spiral order clockwise:
import numpy as np
def spiral_cw(A):
A = np.array(A)
out = []
while(A.size):
out.append(A[0]) # take first row
A = A[1:].T[::-1] # cut off first row and rotate counterclockwise
return np.concatenate(out)
We can write this function on eight different ways depending on where we start and how we rotate the matrix. I'll give another one, which is consistent (it will be evident later) with the matrix transformation in the image in the question. So, further on, I will be using this version:
def spiral_ccw(A):
A = np.array(A)
out = []
while(A.size):
out.append(A[0][::-1]) # first row reversed
A = A[1:][::-1].T # cut off first row and rotate clockwise
return np.concatenate(out)
How it works:
A = np.arange(15).reshape(3,5)
print(A)
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]]
print(spiral_ccw(A))
[ 4 3 2 1 0 5 10 11 12 13 14 9 8 7 6]
Note that the end (or start) point is not the middle cell. This function works for all type of matrices but we will need a helper function that generates spiral indices:
def base_spiral(nrow, ncol):
return spiral_ccw(np.arange(nrow*ncol).reshape(nrow,ncol))[::-1]
For example:
print(base_spiral(3,5))
[ 6 7 8 9 14 13 12 11 10 5 0 1 2 3 4]
Now come the two main functions. One transforms a matrix to a spiral form of the same dimensions, the other reverts the transformation:
def to_spiral(A):
A = np.array(A)
B = np.empty_like(A)
B.flat[base_spiral(*A.shape)] = A.flat
return B
def from_spiral(A):
A = np.array(A)
return A.flat[base_spiral(*A.shape)].reshape(A.shape)
Examples
Matrix 3 x 5:
A = np.arange(15).reshape(3,5)
print(A)
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]]
print(to_spiral(A))
[[10 11 12 13 14]
[ 9 0 1 2 3]
[ 8 7 6 5 4]]
print(from_spiral(to_spiral(A)))
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]]
Matrix from the question:
B = np.arange(1,26).reshape(5,5)
print(B)
[[ 1 2 3 4 5]
[ 6 7 8 9 10]
[11 12 13 14 15]
[16 17 18 19 20]
[21 22 23 24 25]]
print(to_spiral(B))
[[21 22 23 24 25]
[20 7 8 9 10]
[19 6 1 2 11]
[18 5 4 3 12]
[17 16 15 14 13]]
print(from_spiral(to_spiral(B)))
[[ 1 2 3 4 5]
[ 6 7 8 9 10]
[11 12 13 14 15]
[16 17 18 19 20]
[21 22 23 24 25]]
Remark
If you are going to work only with fixed size matrices, for example 5x5, then it's worth replacing base_spiral(*A.shape) in definitions of the functions with a fixed matrix of indices, say Ind (where Ind = base_spiral(5,5)).
Below is python3 code which transforms:
[[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]]
to
[[20, 19, 18, 17, 16],
[21, 6, 5, 4, 15],
[22, 7, 0, 3, 14],
[23, 8, 1, 2, 13],
[24, 9, 10, 11, 12]]
You can easily change implementation in such way how do you want...
def spiral(X, Y):
x = y = 0
dx = 0
dy = -1
for i in range(max(X, Y) ** 2):
if (-X / 2 < x <= X / 2) and (-Y / 2 < y <= Y / 2):
yield x, y
# print(x, y)
# DO STUFF...
if x == y or (x < 0 and x == -y) or (x > 0 and x == 1 - y):
dx, dy = -dy, dx
x, y = x + dx, y + dy
spiral_matrix_size = 5
my_list = list(range(spiral_matrix_size**2))
my_list = [my_list[x:x + spiral_matrix_size] for x in range(0, len(my_list), spiral_matrix_size)]
print(my_list)
for i, (x, y) in enumerate(spiral(spiral_matrix_size, spiral_matrix_size)):
diff = int(spiral_matrix_size / 2)
my_list[x + diff][y + diff] = i
print(my_list)
Here's a solution using itertools and virtually no maths, just observations about what the spiral looks like. I think it's elegant and pretty easy to understand.
from math import ceil, sqrt
from itertools import cycle, count, izip
def spiral_distances():
"""
Yields 1, 1, 2, 2, 3, 3, ...
"""
for distance in count(1):
for _ in (0, 1):
yield distance
def clockwise_directions():
"""
Yields right, down, left, up, right, down, left, up, right, ...
"""
left = (-1, 0)
right = (1, 0)
up = (0, -1)
down = (0, 1)
return cycle((right, down, left, up))
def spiral_movements():
"""
Yields each individual movement to make a spiral:
right, down, left, left, up, up, right, right, right, down, down, down, ...
"""
for distance, direction in izip(spiral_distances(), clockwise_directions()):
for _ in range(distance):
yield direction
def square(width):
"""
Returns a width x width 2D list filled with Nones
"""
return [[None] * width for _ in range(width)]
def spiral(inp):
width = int(ceil(sqrt(len(inp))))
result = square(width)
x = width // 2
y = width // 2
for value, movement in izip(inp, spiral_movements()):
result[y][x] = value
dx, dy = movement
x += dx
y += dy
return result
Usage:
from pprint import pprint
pprint(spiral(range(1, 26)))
Output:
[[21, 22, 23, 24, 25],
[20, 7, 8, 9, 10],
[19, 6, 1, 2, 11],
[18, 5, 4, 3, 12],
[17, 16, 15, 14, 13]]
Here's the same solution shortened:
def stretch(items, counts):
for item, count in izip(items, counts):
for _ in range(count):
yield item
def spiral(inp):
width = int(ceil(sqrt(len(inp))))
result = [[None] * width for _ in range(width)]
x = width // 2
y = width // 2
for value, (dx, dy) in izip(inp,
stretch(cycle([(1, 0), (0, 1), (-1, 0), (0, -1)]),
stretch(count(1),
repeat(2)))):
result[y][x] = value
x += dx
y += dy
return result
I've ignored the fact that you want the input to be a 2D array since it makes much more sense for it to be any 1D iterable. You can easily flatten the input 2D array if you want. I've also assumed the output should be a square since I can't think what you'd sensibly want otherwise. It may go over the edge and raise an error if the square has even length and the input is too long: again, I don't know what the alternative would be.
You can fill an array with somehing like this :
#!/usr/bin/python
class filler:
def __init__(self, srcarray):
self.size = len(srcarray)
self.array = [[None for y in range(self.size)] for y in range(self.size)]
self.xpos, self.ypos = 0, 0
self.directions = [self.down, self.right, self.up, self.left]
self.direction = 0
self.fill(srcarray)
def fill(self, srcarray):
for row in reversed(srcarray):
for elem in reversed(row):
self.array[self.xpos][self.ypos] = elem
self.go_to_next()
def check_next_pos(self):
np = self.get_next_pos()
if np[1] in range(self.size) and np[0] in range(self.size):
return self.array[np[0]][np[1]] == None
return False
def go_to_next(self):
i = 0
while not self.check_next_pos() and i < 4:
self.direction = (self.direction + 1) % 4
i += 4
self.xpos, self.ypos = self.get_next_pos()
def get_next_pos(self):
return self.directions[self.direction](self.xpos, self.ypos)
def down(self, x, y):
return x + 1, y
def right(self, x, y):
return x, y + 1
def up(self, x, y):
return x - 1, y
def left(self, x, y):
return x, y - 1
def print_grid(self):
for row in self.array:
print(row)
f = filler([[x+y*5 for x in range(5)] for y in range(5)])
f.print_grid()
The output of this will be :
[24, 9, 10, 11, 12]
[23, 8, 1, 2, 13]
[22, 7, 0, 3, 14]
[21, 6, 5, 4, 15]
[20, 19, 18, 17, 16]
def counter(n):
for i in range(1,n*n):
yield i+1
n = 11
a = [[1 for x in range(n)] for y in range(n)]
x = y = n//2
val = counter(n)
for i in range(2, n, 2):
y += 1
x -= 1
for k in range(i):
x += 1
a[x][y] = next(val)
for k in range(i):
y -= 1
a[x][y] = next(val)
for k in range(i):
x -= 1
a[x][y] = next(val)
for k in range(i):
y += 1
a[x][y] = next(val)
for i in range(n):
for j in range(n):
print (a[i][j] , end="")
print (" " , end="")
print("\n")
I'm just doing something about generating various spiral indexing of a array and I add some simple modifications to the answer of ptrj to make the function more general. The modified function supports beginning the indexing from the four corners with clockwise and counter-clockwise directions.
def spiral_ind(A,start,direction):
if direction == 'cw':
if start == 'right top':
A = np.rot90(A)
elif start == 'left bottom':
A = np.rot90(A,k=3)
elif start == 'right bottom':
A = np.rot90(A,k=2)
elif direction == 'ccw':
if start == 'left top':
A = np.rot90(A,k=3)
elif start == 'left bottom':
A = np.rot90(A,k=2)
elif start == 'right bottom':
A = np.rot90(A)
out = []
while(A.size):
if direction == 'cw':
out.append(A[0])
A = A[1:].T[::-1]
elif direction == 'ccw':
out.append(A[0][::-1])
A = A[1:][::-1].T
return np.concatenate(out)
def spiral(m):
a=[]
t=list(zip(*m)) # you get the columns by zip function
while m!=[]:
if m==[]:
break
m=list(zip(*t)) # zip t will give you same m matrix. It is necessary for iteration
a.extend(m.pop(0)) # Step1 : pop first row
if m==[]:
break
t=list(zip(*m))
a.extend(t.pop(-1)) # Step 2: pop last column
if m==[]:
break
m=list(zip(*t))
a.extend(m.pop(-1)[::-1]) # Step 3: pop last row in reverse order
if m==[]:
break
t=list(zip(*m))
a.extend(t.pop(0)[::-1]) # Step 4: pop first column in reverse order
return a
This solution is O(n); just one while loop; much faster and can be used for much bigger dimensions of matrices
I had a related problem: I have two digital elevation models that might not be exactly aligned. To check how many cells they're miss-aligned by, I wanted a list of (x,y) offset tuples, starting with the smallest offsets first. I solved the problem by coding a spiral walk that creates square spirals of any size. It can travel either clockwise or counterclockwise. Commented code is below. Similar idea to some of the other solutions, but commented and with a bit less repeated code.
#Generates a list of offsets to check starting with the smallest offsets
#first. Seed the list with (0,0)
to_check = [(0,0)]
#Current index of the "walker"
cur_ind = np.array([0,0])
#Direction to start move along the sides
move_step = 1
#Controls the direction of the spiral
#any odd integer = counter clockwise
#any even integer = clockwise
ctr = 0
#The size of each side of the spiral to be created
size = 5
#Iterate the through the number of steps to take along each side
for i in range(1,size+1):
#Toggle the direction of movement along the sides
move_step *= -1
#Step along each of the two sides that has the same number of
#elements
for _ in range(2):
#Increment the counter (changes whether the x or y index in
#cur_ind is incremented)
ctr += 1
for ii in range(i):
#Move the "walker" in the direction indicated by move_step
#along the side indicated
cur_ind[ctr%2] += move_step
#Add the current location of the water to the list of index
#tuples
to_check.append((cur_ind[0],cur_ind[1]))
#Truncate the list to just the indices to create the spiral size
#requested
to_check = to_check[:size**2]
#Check that the spiral is working
#Create an empty array
arr = np.zeros([size,size])*np.nan
ctr = 1
#for each x,y offset pair:
for dx,dy in to_check:
#Starting at the approximate center of the array, place the ctr
#at the index indicated by the offset
arr[int(size/2)+dx,int(size/2)+dy]=ctr
ctr+=1
print(arr)
The last few lines just display the spiral:
[[13. 14. 15. 16. 17.]
[12. 3. 4. 5. 18.]
[11. 2. 1. 6. 19.]
[10. 9. 8. 7. 20.]
[25. 24. 23. 22. 21.]]

Magic Squares - Siamese Method

Is it possible to do magic squares with the Siamese/De La Loubere method without using modulo?
I would like to make odd n x n magic squares using it.
Yes, it's possible. Written on Python 3.5:
def siamese_method(n):
assert(n % 2 != 0), 'Square side size should be odd!'
square = [[0 for x in range(n)] for x in range(n)]
x = 0
y = int((n + 1) / 2 - 1)
square[x][y] = 1
for i in range(2, n * n + 1):
x_old = x
y_old = y
if x == 0:
x = n - 1
else:
x -= 1
if y == n - 1:
y = 0
else:
y += 1
while square[x][y] != 0:
if x == n - 1:
x = 0
else:
x = x_old + 1
y = y_old
square[x][y] = i
for j in square:
print(j)
siamese_method(3)
I've got following on output:
[8, 1, 6]
[3, 5, 7]
[4, 9, 2]

Categories

Resources