Python Optimizating the Van sequence - python

I am writing a code on python for the platform Coding Games . The code is about Van Eck's sequence and i pass 66% of the "tests".
Everything is working as expected , the problem is that the process runs out of the time allowed.
Yes , the code is slow.
I am not a python writer and I would like to ask you if you could do any optimization on the piece of code and if your method is complex ( Complex,meaning if you will be using something along vectorized data ) and not just swap an if (because that is easily understandable) to give a good explanation for your choice .
Here is my code for the problem
import sys
import math
def LastSeen(array):
startingIndex = 0
lastIndex = len(array) - 1
closestNum = 0
for startingIndex in range(len(array)-1,-1,-1):
if array[lastIndex] == array[startingIndex] and startingIndex != lastIndex :
closestNum = abs(startingIndex - lastIndex)
break
array.append(closestNum)
return closestNum
def calculateEck(elementFirst,numSeq):
number = numSeq
first = elementFirst
result = 0
sequence.append(first)
sequence.append(0)
number -= 2
while number != 0 :
result = LastSeen(sequence)
number -= 1
print(result)
firstElement = int(input())
numSequence = int(input())
sequence = []
calculateEck(firstElement,numSequence)

so here is my code without dictionaries. van_eck contains the sequence in the end. Usually I would use a dict to track the last position of each element to save runtime. Otherwise you would need to iterate over the list to find the last occurence which can take very long.
Instead of a dict, I simply initialized an array of sufficient size and use it like a dict. To determine its size keep in mind that all numbers in the van-eck sequence are either 0 or tell you how far away the last occurrence is. So the first n numbers of the sequence can never be greater than n. Hence, you can just give the array a length equal to the size of the sequence you want to have in the end.
-1 means the element was not there before.
DIGITS = 100
van_eck = [0]
last_pos = [0] + [-1] * DIGITS
for i in range(DIGITS):
current_element = van_eck[i]
if last_pos[current_element] == -1:
van_eck.append(0)
else:
van_eck.append(i - last_pos[current_element])
last_pos[current_element] = i

Related

Trying to solve the n-parenthesis problem - but failing

I am trying to implement a solution to the 'n-parenthesis problem'
def gen_paren_pairs(n):
def gen_pairs(left_count, right_count, build_str, build_list=[]):
print(f'left count is:{left_count}, right count is:{right_count}, build string is:{build_str}')
if left_count == 0 and right_count == 0:
build_list.append(build_str)
print(build_list)
return build_list
if left_count > 0:
build_str += "("
gen_pairs(left_count - 1, right_count, build_str, build_list)
if left_count < right_count:
build_str += ")"
#print(f'left count is:{left_count}, right count is:{right_count}, build string is:{build_str}')
gen_pairs(left_count, right_count - 1, build_str, build_list)
in_str = ""
gen_pairs(n,n,in_str)
gen_paren_pairs(2)
It almost works but isn't quite there.
The code is supposed to generate a list of correctly nested brackets whose count matches the input 'n'
Here is the final contents of a list. Note that the last string starts with an unwanted left bracket.
['(())', '(()()']
Please advise.
Here's a less convoluted approach:
memory = {0:[""]}
def gp(n):
if n not in memory:
local_mem = []
for a in range(n):
part1s = list(gp(a))
for p2 in gp(n-1-a):
for p1 in part1s:
pat = "("+p1+")"+p2
local_mem.append(pat)
memory[n] = local_mem
return memory[n]
The idea is to take one pair of parentheses, go over all the ways to divide the remaining N-1 pairs between going inside that pair and going after it, find the set of patterns for each of those sizes, and make all of the combinations.
To eliminate redundant computation, we save the values returned for each input n, so if asked for the same n again, we can just look it up.

How can I count sequences that meet these constraints?

I am trying to count permutations of a sequence of I and O symbols, representing e.g. people entering (I for "in") and leaving (O for "out") a room. For a given n many I symbols, there should be exactly as many O symbols, giving a total length of 2*n for the sequence. Also, at any point in a valid permutation, the number of O symbols must be less than or equal to the number of I symbols (since it is not possible for someone to leave the room when it is empty).
Additionally, I have some initial prefix of I and O symbols, representing people who previously entered or left the room. The output should only count sequences starting with that prefix.
For example, for n=1 and an initial state of '', the result should be 1 since the only valid sequence is IO; for n=3 and an initial state of II, the possible permutations are
IIIOOO
IIOIOO
IIOOIO
for a result of 3. (There are five ways for three people to enter and leave the room, but the other two involve the first person leaving immediately.)
I'm guessing the simplest way to solve this is using itertools.permutations. This is my code so far:
n=int(input()) ##actual length will be 2*n
string=input()
I_COUNT=string.count("I")
O_COUNT=string.count("O")
if string[0]!="I":
sys.exit()
if O_COUNT>I_COUNT:
sys.exit()
perms = [''.join(p) for p in permutations(string)]
print(perms)
the goal is to get the permutation for whatever is left out of the string and append it to the user's input, so how can I append user's input to the remaining length of the string and get the count for permutation?
#cache
def count_permutations(ins: int, outs: int):
# ins and outs are the remaining number of ins and outs to process
assert outs >= ins
if ins == 0 :
# Can do nothing but output "outs"
return 1
elif outs == ins:
# Your next output needs to be an I else you become unbalanced
return count_permutations(ins - 1, outs)
else:
# Your. next output can either be an I or an O
return count_permutations(ins - 1, outs) + count_permutations(ins, outs - 1)
If, say you have a total of 5 Is and 5 Os, and you've already output one I, then you want: count_permutations(4, 5).
I'm guessing the simplest way to solve this is using itertools.permutations
Sadly, this will not be very helpful. The problem is that itertools.permutations does not care about the value of the elements it's permuting; it treats them as all distinct regardless. So if you have 6 input elements, and ask for length-6 permutations, you will get 720 results, even if all the inputs are the same.
itertools.combinations has the opposite issue; it doesn't distinguish any elements. When it selects some elements, it only puts those elements in the order they initially appeared. So if you have 6 input elements and ask for length-6 combinations, you will get 1 result - the original sequence.
Presumably what you wanted to do is generate all the distinct ways of arranging the Is and Os, then take out the invalid ones, then count what remains. This is possible, and the itertools library can help with the first step, but it is not straightforward.
It will be simpler to use a recursive algorithm directly. The general approach is as follows:
At any given time, we care about how many people are in the room and how many people must still enter. To handle the prefix, we simply count how many people are in the room right now, and subtract that from the total number of people in order to determine how many must still enter. I leave the input handling as an exercise.
To determine that count, we count up the ways that involve the next action being I (someone comes in), plus the ways that involve the next action being O (someone leaves).
If everyone has entered, there is only one way forward: everyone must leave, one at a time. This is a base case.
Otherwise, it is definitely possible for someone to come in. We recursively count the ways for everyone else to enter after that; in the recursive call, there is one more person in the room, and one fewer person who must still enter.
If there are still people who have to enter, and there is also someone in the room right now, then it is also possible for someone to leave first. We recursively count the ways for others to enter after that; in the recursive call, there is one fewer person in the room, and the same number who must still enter.
This translates into code fairly directly:
def ways_to_enter(currently_in, waiting):
if waiting == 0:
return 1
result = ways_to_enter(currently_in + 1, waiting - 1)
if currently_in > 0:
result += ways_to_enter(currently_in - 1, waiting)
return result
Some testing:
>>> ways_to_enter(0, 1) # n = 1, prefix = ''
1
>>> ways_to_enter(2, 1) # n = 3, prefix = 'II'; OR e.g. n = 4, prefix = 'IIOI'
3
>>> ways_to_enter(0, 3) # n = 3, prefix = ''
5
>>> ways_to_enter(0, 14) # takes less than a second on my machine
2674440
We can improve the performance for larger values by decorating the function with functools.cache (lru_cache prior to 3.9), which will memoize results of the previous recursive calls. The more purpose-built approach is to use dynamic programming techniques: in this case, we would initialize 2-dimensional storage for the results of ways_to_enter(x, y), and compute those values one at a time, in such a way that the values needed for the "recursive calls" have already been done earlier in the process.
That direct approach would look something like:
def ways_to_enter(currently_in, waiting):
# initialize storage
results = [[0] * currently_in for _ in waiting]
# We will iterate with `waiting` as the major axis.
for w, row in enumerate(results):
for c, column in enumerate(currently_in):
if w == 0:
value = 1
else:
value = results[w - 1][c + 1]
if c > 0:
value += results[w][c - 1]
results[w][c] = value
return results[-1][-1]
The product() function from itertools will allow you to generate all the possible sequences of 'I' and 'O' for a given length.
From that list, you can filter by the sequences that start with the user-supplied start_seq.
From that list, you can filter by the sequences that are valid, given your rules of the number and order of the 'I's and 'O's:
from itertools import product
def is_valid(seq):
'''Evaluates a sequence I's and O's following the rules that:
- there cannot be more outs than ins
- the ins and outs must be balanced
'''
_in, _out = 0, 0
for x in seq:
if x == 'I':
_in += 1
else:
_out += 1
if (_out > _in) or (_in > len(seq)/2):
return False
return True
# User inputs...
start_seq = 'II'
assert start_seq[0] != 'O', 'Starting sequence cannot start with an OUT.'
n = 3
total_len = n*2
assert len(start_seq) < total_len, 'Starting sequence is at least as big as total number, nothing to iterate.'
# Calculate all possible sequences that are total_len long, as tuples of 'I' and 'O'
seq_tuples = product('IO', repeat=total_len)
# Convert tuples to strings, e.g., `('I', 'O', 'I')` to `'IOI'`
sequences = [''.join(seq_tpl) for seq_tpl in seq_tuples]
# Filter for sequences that start correctly
sequences = [seq for seq in sequences if seq.startswith(start_seq)]
# Filter for valid sequences
sequences = [seq for seq in sequences if is_valid(seq)]
print(sequences)
and I get:
['IIIOOO', 'IIOIOO', 'IIOOIO']
Not very elegant perhaps but this certainly seems to fulfil the brief:
from itertools import permutations
def isvalid(start, p):
for c1, c2 in zip(start, p):
if c1 != c2:
return 0
n = 0
for c in p:
if c == 'O':
if (n := n - 1) < 0:
return 0
else:
n += 1
return 1
def calc(n, i):
s = i + 'I' * (n - i.count('I'))
s += 'O' * (n * 2 - len(s))
return sum(isvalid(i, p) for p in set(permutations(s)))
print(calc(3, 'II'))
print(calc(3, 'IO'))
print(calc(3, 'I'))
print(calc(3, ''))
Output:
3
2
5
5
def solve(string,n):
countI =string.count('I')
if countI==n:
return 1
countO=string.count('O')
if countO > countI:
return 0
k= solve(string + 'O',n)
h= solve(string + 'I',n)
return k+h
n= int(input())
string=input()
print(solve(string,n))
This is a dynamic programming problem.
Given the number of in and out operations remaining, we do one of the following:
If we're out of either ins or outs, we can only use operations of the other type. There is only one possible assignment.
If we have an equal number of ins or outs, we must use an in operation according to the constraints of the problem.
Finally, if we have more ins than outs, we can perform either operation. The answer, then, is the sum of the number of sequences if we choose to use an in operation plus the number of sequences if we choose to use an out operation.
This runs in O(n^2) time, although in practice the following code snippet can be made faster using a 2D-list rather than the cache annotation (I've used #cache in this case to make the recurrence easier to understand).
from functools import cache
#cache
def find_permutation_count(in_remaining, out_remaining):
if in_remaining == 0 or out_remaining == 0:
return 1
elif in_remaining == out_remaining:
return find_permutation_count(in_remaining - 1, out_remaining)
else:
return find_permutation_count(in_remaining - 1, out_remaining) + find_permutation_count(in_remaining, out_remaining - 1)
print(find_permutation_count(3, 3)) # prints 5
The number of such permutations of length 2n is given by the n'th Catalan number. Wikipedia gives a formula for Catalan numbers in terms of central binomial coefficients:
from math import comb
def count_permutations(n):
return comb(2*n,n) // (n+1)
for i in range(1,10):
print(i, count_permutations(i))
# 1 1
# 2 2
# 3 5
# 4 14
# 5 42
# 6 132
# 7 429
# 8 1430
# 9 4862

How to consistently slice the first and last character of a string in Python 3

I need help with writing a loop that can slice through a string, taking away the first and last character. I understand that the slice [0:-1] can target those two positions, but I need a way for it to iterate through the whole string. Here is a little snippet into what the output would ideally look like:
Input (min for length is 3) :
string = 'ABCDEFGHI'
Output:
['ABCDEFGHI', 'BCDEFGH', 'CDEFG']
I would be grateful for any guidance/advice!
Try this:
>>> [string[i:len(string)-i] for i in range(3)]
['ABCDEFGHI', 'BCDEFGH', 'CDEFG']
Here's a function you can use:
def foo(string, limit=None):
output = [string]
i = 0
if limit == None:
while i < len(string) / 2 - 1:
output.append(output[-1][1:-1])
i += 1
else:
while i < limit - 1:
output.append(output[-1][1:-1])
i += 1
return output
The second parameter can be used to set a limit to the length of the array. It is optional.

How to find duplicate values from a new array in Python?

I'm still extremely new to python, and currently stuck on this problem. Basically, I take a list of numbers and add them to each other, starting at zero. The code writes out each line into a new array. If in this new array I find two of the same numbers, it stops and returns that number. The original list of values repeats itself if no duplicate is found.
Here's what I have so far:
file = open("list.txt", "r")
array1 = file.readlines()
total = 0
finalValue = 0
for i in range(0,len(array1)):
array1[i] = int(array1[i])
array2 = []
i = 0
counter = 0
while finalValue == 0:
total += array1[i]
array2.append(total)
print(array2)
for c in range(0,len(array2)):
if (total == array2[c]):
counter += 1
if counter == 2:
finalValue = total
break
if (i == len(array1)-1):
i = 0
else:
i += 1
counter = 0
print(finalValue)
I think the counter is working, but it never finds a duplicate, i.e. it never hits the second counter.
There are plenty of ways to make your code simpler in Python, but first of all, your problem is that the condition total == array2[c] compares elements of the array with your total, not with each other. For example, if your array is [1,3,3], the second 3 would be compared to 4, not to 3.
If I understand your code, I think you want to change total == array2[c] to array1[i] == array2[c] - but that's just an immediate fix, you can use python's list techniques to make this code much simpler.

Read Bits for definded length at defined position

I have a bitstring.Bitarray and want to read from a certain position to another position.
I have the int variable length in a for loop, so for example I have:
length = 2
and my Bitarray looks something like:
msgstr = bitstring.BitArray(0b11110011001111110)
id = bitstring.BitArray()
m = 0
while 5 != m:
/////////////
Length changes in value part of Code
/////////////
x = 0
if m == 0:
while length != x:
id.append = msgstr[x] #msgstr is the BitArray that needs to be read
x = x + 1
m = m + 1
I then want to read the first two bits and convert them into an int, so that I have:
id == 3
And for the next round when length has changed in value it should start from the third bit etc.
The code inside your loop only does anything if m == 0, but then you increment m, so m is only 0 the first time through the loop. The rest of the times you go through your loop, it doesn't seem to actually be doing anything.
Also, where you say
id.append = msgstr[x]
you probably actually want
id.append(msgstr[x])
It also seems like you might benefit from using Python's slice notation.
I do not understand exactly what you goal is but do you had a look at https://wiki.python.org/moin/BitManipulation ?

Categories