Repetitive sequence (optimization) - python-3.x

I try to solve this problem:
initial list = [0, 1, 2, 2]
You get this sequence of numbers [0, 1, 2, 2] and you need to add every time the next natural number (so 3, 4, 5, etc.) n times, where n is the element of its index. For example, the next number to add is 3, and list[3] is 2, so you append [3] 2 times. New list will be: [0, 1, 2, 2, 3, 3]. Then the index of 4 is 3, so you have to append 4 three times. The list will be [0, 1, 2, 2, 3, 3, 4, 4, 4] and so on. ([0, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10])
In order to solve this, I tried various approaches. I used recursion, but a recursive approach is very slow in this case. I tried as well the mathematical formula from OEIS (A055086) => a(n) = ceiling(2*sqrt(n+1)) - 2. The problem with the formula is that after 2 ** 20 it is too imprecise.
So, my next idea was to use memoization:
lst = [0, 1, 2, 2]
from itertools import repeat
def find(n):
global lst
print(lst[-1], n, flush = True)
if len(lst) > n:
return lst[n]
for number in range(lst[-1]+1, n+1):
lst += list(repeat(number, lst[number]))
if len(lst) > n:
return lst[n]
Now, this approach works until 2 ** 37, but after this is just timing out. The site where I try to implement my algorithm is (https://www.codewars.com/kata/5f134651bc9687000f8022c4/train/python). I don't ask for a solution, but for any hint on how to optimize my code.
I googled some similar problems and I found that in this case, I could use the total sum of the list, but is not very clear to me yet how could this help me.
Any help is welcomed!

You can answer it iteratively like so:
def find(n):
lst = [0,1,2,2]
if n < 4:
return lst[n]
to_add = 3
while n >= len(lst):
for i in range(lst[to_add]):
lst.append(to_add)
to_add += 1
return lst[n]
You could optimise for large n by breaking early in the for loop, and by keeping track of the list length separately, rather than calls to len

Related

Vector to an array of vectors of neighbours

I'd like to take a vector and get an array of vectors in which the i-th element of each vector are the k neighbors of the i-th element of the original vector. Also, I'm looking for the fastest way to do so.
I've already done that in MATLAB:
a=zeros(k, length(v));
I=cell(1,k);
a(1,:) = v;
for j=2:k
a(k,:)=[a(k-1,2:end),a(k-1,1)];
end
aux1=[a(:,(end-r+1):end),a(:,1:(end-r))];
for j=1:k
I{k}=aux1(k,:);
end
For example, v = [1, 2, 3, 4, 5] and k = 1; and I want to get:
M = [[5, 1, 2, 3, 4], [1, 2, 3, 4, 5], [2, 3, 4, 5, 1]]
so that, for the 1st element of each vector, I get [5; 1; 2], which are the element 1 and its neighbors.
Hope it makes sense. Thanks for reading :)
You could use the numpy roll function:
import numpy as np
def get_neighbors(v, k):
N = len(v)
M = np.zeros((k*2+1, N), dtype=int)
for i in range(-k, k+1):
M[i+k, :] = np.roll(v, -i)
return M
v = np.array([1, 2, 3, 4, 5])
k = 1
M = get_neighbors(v, k)
print(M)
Output:
[[5 1 2 3 4]
[1 2 3 4 5]
[2 3 4 5 1]]
Using sliding_window_view on a repetition of your array can do it "vectorized" way
# Example array
a = np.arange(1,16)
k = 2 # Window of neighbors
# My solution
np.lib.stride_tricks.sliding_window_view(np.hstack([a,a,a]), (len(a),))[len(a)-k:len(a)+k+1]
Returns
array([[14, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
[15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14],
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1],
[ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1, 2]])
Note that sliding_window_view creates just a view. It doesn't create new data. Hence the reason why I do not hesitate creating (in this example) 31 lines (3*15-15+1), and then subset only 5 of them: I do not really create them.
So only real cost of that solution is in hstack, both cpu-wise and memory-wise.
That subset, btw, was done to abide strictly by what you asked. But, depending on what you intend to do, you may drop the subset. Important point is that if
T=np.lib.stride_tricks.sliding_window_view(np.hstack([a,a,a]), (len(a),))
Then T[len(a)+k] is a row made of the kth neighbor, whether k is positive, negative or 0 (the original row)
See timings, since it matters for you
sizes
This method
Roll method
len=15/k=2
51 μs
132 μs
len=15/k=7
51 μs
383 μs
len=1000/k=7
52 μs
422 μs
len=1M/k=7
6 ms
160 ms
len=1M/k=100
6 ms
2.2 s
Roll method is obviously proportional to the size of the window (O(k) — it has one roll to perform per row of output), when sliding_window_view is just a view, and does not really create rows, so is O(1) as far as k is concerned. Both method are equally impacted by len of data (O(n) really, but it shows only for n big enough).
So, all together, this method is O(n) while roll method is O(kn)

Cyclic Rotation of Numbers in a List in Python

I am trying to do cyclic rotation of numbers in List in Python.
For example,
An array A consisting of N integers is given. Rotation of the array means that each element is shifted right by one index, and the last element of the array is moved to the first place. For example, the rotation of array A = [3, 8, 9, 7, 6] is [6, 3, 8, 9, 7] (elements are shifted right by one index and 6 is moved to the first place).
The goal is to rotate array A K times; that is, each element of A will be shifted to the right K times.
For example, given
A = [3, 8, 9, 7, 6]
K = 3
the function should return [9, 7, 6, 3, 8]. Three rotations were made:
[3, 8, 9, 7, 6] -> [6, 3, 8, 9, 7]
[6, 3, 8, 9, 7] -> [7, 6, 3, 8, 9]
[7, 6, 3, 8, 9] -> [9, 7, 6, 3, 8]
I have written a function that is supposed to do the above task. Here is my code:
def sol(A, K):
for i in range(len(A)):
if (i+K) < len(A):
A[i+K] = A[i]
else:
A[i+K - len(A)] = A[i]
return A
A = [3, 8, 9, 7, 6]
K = 3
# call the function
sol(A,K)
[9, 3, 8, 3, 8]
I am getting [9, 3, 8, 3, 8] instead of [9, 7, 6, 3, 8].
Can anyone help me with the above code ?
Thanks.
Let's take a look at what happens if K = 1, on just the first iteration:
def sol(A, K):
for i in range(len(A)): # i = 0
if (i+K) < len(A): # i + 1 < 5
A[i+K] = A[i] # A[1] = A[0]
else:
A[i+K - len(A)] = A[i]
# A is now equal to [3, 3, 9, 7, 6] - the element at A[1] got overwritten
return A
The problem is that you don't have anywhere to store the elements you'd be overwriting, and you're overwriting them before rotating them. The ideal solution is to create a new list, populating it with rotated elements from the previous list, and return that. If you need to modify the old list, you can copy elements over from the new list:
def sol(A, K):
ret = []
for i in range(len(A)):
if (i + K) < len(A):
ret.append(A[i + K])
else:
ret.append(A[i + K - len(A)])
return ret
Or, more concisely (and probably how your instructor would prefer you solve it), using the modulo operator:
def sol(A, K):
return [
A[(i + K) % len(A)]
for i in range(len(A))
]
Arguably the most pythonic solution, though, is to concatenate two list slices, moving the part of the list after index K to the front:
def sol(A, K):
return A[K % len(A):] + A[:K % len(A)]

How to sum an output in Python

The program below will create a list of 100 numbers chosen randomly between 1-10. I need help to then sum the list, then average the list created.
I have no idea how to begin and since I'm watching videos online I have no person to turn to. I'm very fresh in this world so I may just be missing entire ideas. I would doubt that I don't actually know enough though because the videos I paid for are step by step know nothing to know something.
Edit: I was informed that what the program does is overwrite a variable, not make a list. So how do I sum my output like this example?
This is all I have to go on:
Code:
import random
x=0
while x < 100:
mylist = (random.randrange(1,10))
print(mylist)
x = x+1
I think the shortest and pythonic way to do this is:
import random
x = [random.randrange(1,10) for i in range(100)] #list comprehension
summed = sum(x) #Sum of all integers from x
avg = summed / len(x) #Average of the numbers from x
In this case this shouldn't have a big impact, but you should never use while and code manual counter when you know how many times you want to go; in other words, always use for when it's possible. It's more efficient and clearer to see what the code does.
def sum(list):
sm = 0
for i in list:
sm+=i
return sm
Just run sum(list) to get sum of all elements
Or you can use
import random
x=0
mylist = []
sm = 0
while x < 100:
mylist.append(random.randrange(1,10))
sm += mylist[x]
x += 1
Then sm will be sum of list
The code is not correct. It will not create a list but generate a number everytime. Use the below code to get your desired result.
import random
mylist = []
for x in range(100):
mylist.append(random.randrange(1,10))
print(mylist)
print(sum(mylist))
OR
import random
mylist = [random.randrange(1,10) for value in range(100)]
print(mylist)
print(sum(mylist))
Output:
[3, 9, 3, 1, 3, 5, 8, 8, 3, 3, 1, 2, 5, 1, 2, 1, 4, 8, 9, 1, 2, 2, 4,
6, 9, 7, 9, 5, 4, 5, 7, 7, 9, 2, 5, 8, 2, 4, 3, 8, 2, 1, 3, 4, 2, 2,
2, 1, 6, 8, 3, 2, 1, 9, 6, 5, 8, 7, 7, 9, 9, 9, 8, 5, 7, 9, 4, 9, 8,
7, 5, 9, 2, 6, 8, 8, 3, 4, 8, 4, 7, 9, 9, 4, 2, 9, 9, 6, 3, 4, 9, 5,
3, 8, 4, 1, 1, 3, 2, 6]
512

How to find all numbers in a list that are not part of a pair - using python 3

I am trying to write a python 3 function that finds all numbers in a list (unspecified length) that are not part of a pair.
For example, given the list [1, 2, 1, 3, 2], the function will return 3; and given the list [0, 1, 1, 7, 8, 3, 9, 3, 9], the function will return 0, 7, and 8.
Thanks for your help!
You can use the following function :
>>> def find(l):
... return (i for i in l if l.count(i)==1)
>>> l= [0, 1, 1, 7, 8, 3, 9, 3, 9]
>>> list(find(l))
[0, 7, 8]
This function will return a generator that is contain the elements in list which those count is equal to 1.
I can tell you how I would do it. What does it mean a "pair"?
You should say, find all the numbers repeated oddly in the array.
First plan: (more efficient!)
Sort the list and then a single loop through your list should be enough to find how many numbers of each there are inside and you can generate awhile another list that you will return.
Second plan (nicer in python, but also more expensive because of the number of evaluations though the hole list):
Try the solution of Kasra. 'count' function from 'list' type helps our code but not our efficiency. It counts the number of times that appears the value 'i' on the list 'l', obviously.
If the pair need to be "closed pair" I mean, if you have three 1 (ones), do you have one pair and one single 1? or do you have all the 1 paired? If the second one, the solution of Kasra is Ok. Else you should compare:
if l.count(i) % 2 == 1
This can be easily and efficiently done in 3 lines with collections.Counter.
from collections import Counter
def unpaired(numbers):
for key, count in Counter(numbers).items():
if count % 2:
yield key
print(list(unpaired([1, 2, 1, 3, 2])))
# [3]
print(list(unpaired([0, 1, 1, 7, 8, 3, 9, 3, 9])))
# [0, 7, 8]
My answer comport if you have three equals numbers or if you have one pair and one single number without pair.
def name(array):
o = sorted(array)
c = []
d = []
for i in o:
if o.count(i) % 2 == 1:
c.append(i)
for j in c:
if j not in d:
d.append(j)
return d
or do not use for j in c and use directly:
return list(set(c))
for example:
array = [0, 1, 1, 7, 8, 3, 9, 3, 9, 9]
output: [0, 7, 8, 9]

print the longest sequence of number in number list (python)

I have a list of number:
a=[2,3,4,5,1,3,2,4,5,6,2,6,7,5,2,7,5,6,2]
I want the longest sequence that not contain 2, so the answer is:
[3,4,5,1,3]
How can I do this in python?
Thanks for helping me,
You can use itertools.groupby():
from itertools import groupby
a = [2, 3, 4, 5, 1, 3, 2, 4, 5, 6, 2, 6, 7, 5, 2, 7, 5, 6, 2]
# get the subsequences not containing 2
subsequences = (list(it)
for contains_two, it in groupby(a, lambda x: x == 2)
if not contains_two)
# find the longest one among them
print(max(subsequences, key=len))
prints
[3, 4, 5, 1, 3]

Resources