Arithmetic Sequences Slices in Python - python

I'm trying to write a function that takes a list of integers and finds all arithmetic sequences in it.
A = [-1, 1, 3, 3, 3, 2, 1, 0]
There are five arithmetic sequences in this list: (0, 2), (2,4), (4, 6), (4,7), (5,7) - these are indexes of first and last element of a sequence. A sequence is derived by the difference between elements.
As you see from the example above - the sequence must be longer than 2 elements (otherwise it would find a sequence between every two elements).
The function that I need to write must return the number of sequences it finds on the list - in this case it should return 5.
I'm kind of stuck - tried a few different approaches but failed miserably. The most recent thing I've done is:
def solution(A):
slices = []
x = 0
listlen = len(A)
while x < listlen-1:
print ("Current x:", x)
difference = A[x+1] - A[x]
#print ("1st diff: ", A[x+1], ",", A[x], " = ", difference)
for y in range(x+1, len(A)-1):
difference_2 = A[y+1] - A[y]
#print ("Next Items: ", A[y+1], A[y])
#print ("2nd diff: ", difference_2)
if (difference == difference_2):
#print ("I'm in a sequence, first element at index", x)
else:
#print ("I'm leaving a sequence, last element at index ", y)
slice = str(x) + "," + str(y)
slices.append(slice)
x += 1
#print ("Changing X to find new slice: x:", x)
break
print (slices)
I messed something up with iterating X, at this point in time, it's an endless loop.

Maybe you can use a logic like this -
>>> A = [-1, 1, 3, 3, 3, 2, 1, 0]
>>> def indices(l):
... res = []
... for i in range(0,len(l)-2):
... diff = l[i+1] - l[i]
... for j in range(i+2,len(l)):
... if (l[j] - l[j-1]) == diff:
... res.append((i,j))
... else:
... break;
... return res
...
>>> indices(A)
[(0, 2), (2, 4), (4, 6), (4, 7), (5, 7)]

a brute force approach is to just check each slice > len 3, for each slice you just need to subtract the first and last element to get the difference and see if all a[i+1] - A[i] are equal to the difference:
def is_arith(x):
return all(x[i + 1] - x[i] == x[1] - x[0]
for i in range(len(x) - 1))
def arith_count(A):
return sum(is_arith(A[i:j])for i in range(len(A))
for j in range(i + 3,len(A)+1))
A more efficient version:
def arith_sli(A):
n = len(A)
st,tot = 0, 0
while st < n - 2:
end = st + 1
dif = A[end] - A[st]
while end < n - 1 and A[end + 1] - A[end] == dif:
end += 1
ln = end - st + 1
if ln >= 3:
tot += (ln - 2) * (ln - 1) // 2
st = end
return tot
tot += (ln - 2) * (ln - 1) // 2 is the max number of slices that can be formed for any length progression >= 3, we set st = end because no progressions can overlap.
Both return the correct output, the latter is just considerably more efficient:
In [23]: A = [-1, 1, 3, 3, 3, 2, 1, 0]
In [24]: arith_sli(A)
Out[24]: 5
In [25]: arith_count(A)
Out[25]: 5
In [26]: A = [-1, 1, 3, 3, 4, 2, 1, 0,1,2]
In [27]: arith_sli(A)
Out[27]: 3
In [28]: arith_count(A)
Out[28]: 3

Related

algorithm to efficiently traverse or generate all list indices

there is an issue that i am trying to solve which requires me to generate the indices for an n - dimensional list. Eg: [5, 4, 3] is a 3 dimensional list so valid indices are [0, 0, 0], [0, 0, 1], [0, 1, 0] ... [2, 2, 1] ..... [4, 3, 2]. The best I could come up with a recursive algorithm but this isn't constant space
def f1(dims):
def recur(res, lst, depth, dims):
if depth == len(dims):
res.append(lst[::])
return
curr = dims[depth]
for i in range(curr):
lst[depth] = i
recur(res, lst, depth + 1, dims)
res = []
lst = [0] * len(dims)
recur(res, lst, 0, dims)
return res
the dimensions can be any number , ie: 4D, 5D, 15D etc. Each time it would be given in the form of a list . Eg: 5D would be [3,2,1,5,2] and I would need to generate all the valid indices for these while using constant space ( just while loops and indices processing ) . How would I go about generating these efficiently without the help of any in built python functions ( just while, for loops etc )
This is a working solution in constant space (a single loop variable i, and a vector idx of which modified copies are being yielded, and a single temporary length variable n to avoid calling len() on every iteration):
def all_indices(dimensions):
n = len(dimensions)
idx = [0] * n
while True:
for i in range(n):
yield tuple(idx)
if idx[i] + 1 < dimensions[i]:
idx[i] += 1
break
else:
idx[i] = 0
if not any(idx):
break
print(list(all_indices([3, 2, 1])))
Result:
[(0, 0, 0), (1, 0, 0), (2, 0, 0), (0, 0, 0), (0, 1, 0), (1, 1, 0), (2, 1, 0), (0, 1, 0), (0, 0, 0)]
As pointed out in the comments, there's duplicates there, a bit sloppy, this is cleaner:
def all_indices(dimensions):
n = len(dimensions)
idx = [0] * n
yield tuple(idx) # yield the initial 'all zeroes' state
while True:
for i in range(n):
if idx[i] + 1 < dimensions[i]:
idx[i] += 1
yield tuple(idx) # yield new states
break
else:
idx[i] = 0 # no yield, repeated state
if not any(idx):
break
print(list(all_indices([3, 2, 1])))
Alternatively, you could yield before the break instead of at the start of the loop, but I feel having the 'all zeroes' at the start looks cleaner.
The break is there to force a depth first on running through the indices, which ensures the loop never reaches 'all zeroes' again before having passed all possibilities. Try removing the break and then passing in something like [2, 1, 2] and you'll find it is missing a result.
I think a break is actually the 'clean' way to do it, since it allows using a simple for instead of using a while with a more complicated condition and a separate increment statement. You could do that though:
def all_indices3(dimensions):
n = len(dimensions)
idx = [1] + [0] * (n - 1)
yield tuple([0] * n)
while any(idx):
yield tuple(idx)
i = 0
while i < n and idx[i] + 1 == dimensions[i]:
idx[i] = 0
i += 1 % n
if i < n:
idx[i] += 1
This has the same result, but only uses while, if and yields the results in the same order.

Python Knapsack Problem with fixed number of elements

So far I have the following code which I believe selects up to a MAX of 4 (or n) elements in the knapsack (hence the 3rd dimension). However, I want to ensure that the code ALWAYS selects 4 (or n) elements. Can someone please advise as I can't find anything about this anywhere...
def knapsack2(n, weight, count, values, weights):
dp = [[[0] * (weight + 1) for _ in range(n + 1)] for _ in range(count + 1)]
for z in range(1, count + 1):
for y in range(1, n + 1):
for x in range(weight + 1):
if weights[y - 1] <= x:
dp[z][y][x] = max(dp[z][y - 1][x],
dp[z - 1][y - 1][x - weights[y - 1]] + values[y - 1])
else:
dp[z][y][x] = dp[z][y - 1][x]
return dp[-1][-1][-1]
w = 10
k = 4
values = [1, 2, 3, 2, 2]
weights = [4, 5, 1, 1, 1]
n = len(values)
# find elements in
elements=[]
dp=m
while (n> 0):
if dp[k][n][w] - dp[k][n-1][w - weights[n-1]] == values[n-1]:
#the element 'n' is in the knapsack
elements.append(n)
n = n-1 #//only in 0-1 knapsack
w -= weights[n]
else:
n = n-1

Get Next and Previous for every element in list python

I have a list:
day_list = [1,2,3,4,5,6]
I want to write a function which will take any number (no) from this list and return the next and previous element.
So, for example, if I pass 2, I should get [1,3] (1 being the previous and 3 the next elements).
But there is a catch:
If no = 1 the previous element should be 6 and similarly for no = 6 next should be 1
I would like something like:
def getPrevNext(no):
....
....
return [prev,next]
result = getPrevNext(2) # result should be [1,3]
result2 = getPrevNext(5) # result2 should be [4,5]
result3 = getPrevNext(1) # result3 should be [6,2]
result4 = getPrevNext(6) # result3 should be [5,1]
I tried:
def dummy(no):
if no == 1:
prev_no = 6
next_no = 2
elif no == 6:
prev_no = 5
next_no = 1
else:
prev_no = no - 1
next_no = no + 1
return [prev_no,next_no]
But this seems like a very naive and basic approach.. Is there a better way to do this?
FYI List of days not required, that was just for understanding the total no.
#juanpa.arrivillaga's answer covers the specifics of this question quite well (which is a question on how to use modulo arithmetic). However, in the general case of accessing the previous and next elements of any list, this is how I'd do it -
def getPrevNext(l, no):
i = l.index(no)
return [l[i - 1], l[(i + 1) % len(l)]]
days = list(range(1, 7))
getPrevNext(days, 2)
[1, 3]
getPrevNext(days, 1)
[6, 2]
getPrevNext(days, 6)
[5, 1]
The first expression l[i - 1] takes advantage python's ability to access elements using negative indices. The second expression, l[(i + 1) % len(l)], is a common circular list access idiom.
To return a tuple instead of a list, drop the enclosing square brackets in the return statement -
def getPrevNextAsTuple(l, no):
i = l.index(no)
return l[i - 1], l[(i + 1) % len(l)]
Note that this does not handle the possibility of no not being in the list. In that case, you'd use something like exception handling to catch any ValueErrors raised -
def getPrevNext(l, no):
try:
i = l.index(no)
except ValueError:
return None
return l[i - 1], l[(i + 1) % len(l)]
If you are working with "days" encoded 1-6, use the following:
>>> def prev_next_day(day):
... day -= 1
... prev = ((day - 1) % 6) + 1
... next_ = ((day + 1) % 6) + 1
... return [prev, next_]
...
>>> prev_next_day(2)
[1, 3]
>>> prev_next_day(5)
[4, 6]
>>> prev_next_day(1)
[6, 2]
>>> prev_next_day(6)
[5, 1]
You can just use slicing (and optionally, for elegance, mod) like so:
day_list = [1,2,3,4,5,6]
def get_prev_next(my_list, no):
try:
pointer = my_list.index(no)
except ValueError:
return []
else:
return [my_list[pointer-1], my_list[(pointer+1) % len(my_list)]]
Examples:
# first element
print(get_prev_next(day_list, 1)) # -> [6, 2]
# last element
print(get_prev_next(day_list, 6)) # -> [5, 1]
# any other
print(get_prev_next(day_list, 3)) # -> [2, 4]
# non-existent element
print(get_prev_next(day_list, 98)) # -> []
I would say the following works:
def getPrevNext(no):
day_list = [1, 2, 3, 4, 5, 6]
return([day_list[(no-2) % 6], day_list[(no) % 6]])
Depending on whether or not you also only accept 1 <= no <= 6, you could add a condition to this function like so:
def getPrevNext(no):
day_list = [1, 2, 3, 4, 5, 6]
return([day_list[(no-2) % 6], day_list[(no) % 6]] if 1 <= no <= 6 else 0)
def getem(lst,element):
index=lst.index(element)
try:
if index!=0 and index!=(len(lst)-1):
return [lst[index-1],lst[index+1]]
else:
if index==0:
return [lst[len(lst)-1],lst[index+1]]
else:
return [lst[index-1],lst[0]]
except Exception as e:
print("element does not exists")
try this function or you can use circular linked-list in this function i have mimicked that behaviour
Are you sure you need a list of days?
def f(n):
assert 0<n<7, "numbers should be between 1 and 6"
return n-1 if n!=1 else 6, n+1 if n!=6 else 1
f(1)
#result (6, 2)
I think the trick you mentioned is famous as circular linked list. Please correct me if I'm wrong about it.
day_list = [1,2,3,4,5,6]
def get_prev_next(elem):
if elem not in day_list:
return False # element not found
index = day_list.index(elem)
return day_list[i - 1], l[(i + 1) % len(day_list)]
For the previous, you can take advantage of the fact that when indexing a list, negative indexes mean "negative starting on the last element". It helps me thinking about the [-1] as "overflowing", which means going to the first element (index 0) and start counting from the last element.
For the next, you can use the modulo (%) operator, to make sure you keep your indexes between 0 and len(days_list). This is only critical in the case the index of the number you want to calculate the next is the last element of the list (index 5, value 6 in the day_list). Just adding +1 to the index 5 would put you in 6, which is not a valid index. But then you get the module of 6 % 6 and "becomes" 0.
day_list = [1, 2, 3, 4, 5, 6]
def find(thing):
position = day_list.index(thing)
prev_pos = day_list[position - 1]
next_pos = day_list[(position + 1) % len(day_list)]
return prev_pos, next_pos
if __name__ == "__main__":
for i in range(1, 7):
print("prev_next %s: %s" % (i, find(i)))
Outputs:
prev_next 1: (6, 2)
prev_next 2: (1, 3)
prev_next 3: (2, 4)
prev_next 4: (3, 5)
prev_next 5: (4, 6)
prev_next 6: (5, 1)

Random sequence and random position? [duplicate]

How can I randomly shuffle a list so that none of the elements remains in its original position?
In other words, given a list A with distinct elements, I'd like to generate a permutation B of it so that
this permutation is random
and for each n, a[n] != b[n]
e.g.
a = [1,2,3,4]
b = [4,1,2,3] # good
b = [4,2,1,3] # good
a = [1,2,3,4]
x = [2,4,3,1] # bad
I don't know the proper term for such a permutation (is it "total"?) thus having a hard time googling. The correct term appears to be "derangement".
After some research I was able to implement the "early refusal" algorithm as described e.g. in this paper [1]. It goes like this:
import random
def random_derangement(n):
while True:
v = [i for i in range(n)]
for j in range(n - 1, -1, -1):
p = random.randint(0, j)
if v[p] == j:
break
else:
v[j], v[p] = v[p], v[j]
else:
if v[0] != 0:
return tuple(v)
The idea is: we keep shuffling the array, once we find that the permutation we're working on is not valid (v[i]==i), we break and start from scratch.
A quick test shows that this algorithm generates all derangements uniformly:
N = 4
# enumerate all derangements for testing
import itertools
counter = {}
for p in itertools.permutations(range(N)):
if all(p[i] != i for i in p):
counter[p] = 0
# make M probes for each derangement
M = 5000
for _ in range(M*len(counter)):
# generate a random derangement
p = random_derangement(N)
# is it really?
assert p in counter
# ok, record it
counter[p] += 1
# the distribution looks uniform
for p, c in sorted(counter.items()):
print p, c
Results:
(1, 0, 3, 2) 4934
(1, 2, 3, 0) 4952
(1, 3, 0, 2) 4980
(2, 0, 3, 1) 5054
(2, 3, 0, 1) 5032
(2, 3, 1, 0) 5053
(3, 0, 1, 2) 4951
(3, 2, 0, 1) 5048
(3, 2, 1, 0) 4996
I choose this algorithm for simplicity, this presentation [2] briefly outlines other ideas.
References:
[1] An analysis of a simple algorithm for random derangements. Merlini, Sprugnoli, Verri. WSPC Proceedings, 2007.
[2] Generating random derangements. Martínez, Panholzer, Prodinger.
Such permutations are called derangements. In practice you can just try random permutations until hitting a derangement, their ratio approaches the inverse of 'e' as 'n' grows.
As a possible starting point, the Fisher-Yates shuffle goes like this.
def swap(xs, a, b):
xs[a], xs[b] = xs[b], xs[a]
def permute(xs):
for a in xrange(len(xs)):
b = random.choice(xrange(a, len(xs)))
swap(xs, a, b)
Perhaps this will do the trick?
def derange(xs):
for a in xrange(len(xs) - 1):
b = random.choice(xrange(a + 1, len(xs) - 1))
swap(xs, a, b)
swap(len(xs) - 1, random.choice(xrange(n - 1))
Here's the version described by Vatine:
def derange(xs):
for a in xrange(1, len(xs)):
b = random.choice(xrange(0, a))
swap(xs, a, b)
return xs
A quick statistical test:
from collections import Counter
def test(n):
derangements = (tuple(derange(range(n))) for _ in xrange(10000))
for k,v in Counter(derangements).iteritems():
print('{} {}').format(k, v)
test(4):
(1, 3, 0, 2) 1665
(2, 0, 3, 1) 1702
(3, 2, 0, 1) 1636
(1, 2, 3, 0) 1632
(3, 0, 1, 2) 1694
(2, 3, 1, 0) 1671
This does appear uniform over its range, and it has the nice property that each element has an equal chance to appear in each allowed slot.
But unfortunately it doesn't include all of the derangements. There are 9 derangements of size 4. (The formula and an example for n=4 are given on the Wikipedia article).
This should work
import random
totalrandom = False
array = [1, 2, 3, 4]
it = 0
while totalrandom == False:
it += 1
shuffledArray = sorted(array, key=lambda k: random.random())
total = 0
for i in array:
if array[i-1] != shuffledArray[i-1]: total += 1
if total == 4:
totalrandom = True
if it > 10*len(array):
print("'Total random' shuffle impossible")
exit()
print(shuffledArray)
Note the variable it which exits the code if too many iterations are called. This accounts for arrays such as [1, 1, 1] or [3]
EDIT
Turns out that if you're using this with large arrays (bigger than 15 or so), it will be CPU intensive. Using a randomly generated 100 element array and upping it to len(array)**3, it takes my Samsung Galaxy S4 a long time to solve.
EDIT 2
After about 1200 seconds (20 minutes), the program ended saying 'Total Random shuffle impossible'. For large arrays, you need a very large number of permutations... Say len(array)**10 or something.
Code:
import random, time
totalrandom = False
array = []
it = 0
for i in range(1, 100):
array.append(random.randint(1, 6))
start = time.time()
while totalrandom == False:
it += 1
shuffledArray = sorted(array, key=lambda k: random.random())
total = 0
for i in array:
if array[i-1] != shuffledArray[i-1]: total += 1
if total == 4:
totalrandom = True
if it > len(array)**3:
end = time.time()
print(end-start)
print("'Total random' shuffle impossible")
exit()
end = time.time()
print(end-start)
print(shuffledArray)
Here is a smaller one, with pythonic syntax -
import random
def derange(s):
d=s[:]
while any([a==b for a,b in zip(d,s)]):random.shuffle(d)
return d
All it does is shuffles the list until there is no element-wise match. Also, be careful that it'll run forever if a list that cannot be deranged is passed.It happens when there are duplicates. To remove duplicates simply call the function like this derange(list(set(my_list_to_be_deranged))).
import random
a=[1,2,3,4]
c=[]
i=0
while i < len(a):
while 1:
k=random.choice(a)
#print k,a[i]
if k==a[i]:
pass
else:
if k not in c:
if i==len(a)-2:
if a[len(a)-1] not in c:
if k==a[len(a)-1]:
c.append(k)
break
else:
c.append(k)
break
else:
c.append(k)
break
i=i+1
print c
A quick way is to try to shuffle your list until you reach that state. You simply try to shuffle your list until you are left with a list that satisfies your condition.
import random
import copy
def is_derangement(l_original, l_proposal):
return all([l_original[i] != item for i, item in enumerate(l_proposal)])
l_original = [1, 2, 3, 4, 5]
l_proposal = copy.copy(l_original)
while not is_derangement(l_original, l_proposal):
random.shuffle(l_proposal)
print(l_proposal)

Fill in an array using loop with multiple variables (new to Python, old to C++ (back in the day))

Basically what I want to do is create something like this in python (this is basic idea and not actual code):
n = 3
i = n + 1
a = [1, 3, 3, 1]
b = [1, 2, 1]
while n > 1:
Check if n is even
- if n is even, then for all i in range(0,n), insert values into an array using the formula below
- b[n-i] = a[n-i-1] + a[n-i], this value will replace the previously given value of b[] above the code.
- Print out the array
- After each area is filled, n+=1, i=n+1 are applied, then the loop continues
Check if n is odd
- same process except formula is
- a[n-i] = b[n-i-1] + a[n-i], this value will replace the previously given value of a[] above the code.
- Print out the array
- After each area is filled, n+=1, i=n+1 are applied, then the loop continues
This process will loop and print each and continue on, the arrays will essentially look like this:
b = [1, 4, 6, 4, 1], a = [1 5, 10, 10, 5, 1], b = [1, 6, 15, 20, 20, 15, 6, 1], etc.
Here is the code that I currently have, however I'm getting an 'out of range' error.
n = 3
i = n + 1
b = [1, 2, 1]
a = [1, 3, 3, 1]
while n > 1:
if n%2==0:
print("even")
for i in range(0,n):
b[n-i].append(a[n-i-1]+a[n-i])
else:
print("odd")
for i in range(0,n):
print("yay")
a[n-i].append(b[n-i-1]+b[n-i])
if n%2==0:
print(b)
else:
print(a)
n +=1
i = n + 1
print("loop")
The random prints throughout the code are to test and see if it is even making it into the process. There were from a previous code and I just haven't removed them yet.
Hopefully you can help me, I can't find anything online about a loop that constantly increases the size of an array and fills it at the same time.
Sorry struggling with the code that's in the sample. From your description I can see that you want to generate Pascal's triangle. Here's a short snippet that will do this.
a = [1, 1]
for _ in range(10):
a = [1] + [x+y for (x,y) in zip(a[:-1], a[1:])] + [1]
print a
a[:-1] refers to the whole array except the last element and a[1:] refers to whole array except first element. zip combines first elements from each array into a tuple and so on. All that remains is to add them and pad the row with ones one the outside. _ is used to tell Python, I don't care about this variable - useful if you want to be explicit that you are not using the range value for anything except flow control.
Maria's answer is perfect, I think. If you want to start with your code, you can rewrite your code as below to get similar result. FYI.
n = 3
b = [1, 2, 1]
while 1 < n < 10:
if n % 2 == 0:
print("even")
b = [0] * (n + 1)
for i in range(0, n + 1):
if i == 0:
b[i] = a[0]
elif i == n:
b[i] = a[i - 1]
else:
b[n - i] = a[i - 1] + a[i]
else:
print("odd")
a = [0] * (n + 1)
for i in range(0, n + 1):
if i == 0:
a[i] = b[0]
elif i == n:
a[i] = b[i - 1]
else:
a[i] = b[i - 1] + b[i]
if n % 2 == 0:
print(b)
else:
print(a)
n += 1
print("loop")

Categories