Codility NailingPlanks - Using Binary Search on Nail Count Instead of Planks - python

I have already read through the answers on this question - Codility NailingPlanks.
This is not a duplicate, as I'm trying to solve this problem using a different approach - instead of running a binary search on the planks that a given nail can cover, I'm trying to run it on the total number of nails required to cover all the planks.
This is my code:
def solution(A, B, C):
min_nails = 1
max_nails = len(C)
valid = []
while (min_nails <= max_nails):
mid_nails = (min_nails + max_nails) // 2
if (is_valid_nails_amount(mid_nails,A,B,C)):
max_nails = mid_nails - 1
valid.append(mid_nails)
else:
min_nails = mid_nails + 1
return -1 if len(valid) == 0 else min(valid)
def is_valid_nails_amount(nails,A,B,C):
candidates=C[0:nails]
min_cover = min(candidates)
max_cover = max(candidates)
isValid = True
for (a,b) in zip(A,B):
if not(min_cover in range(a, b + 1) or max_cover in range(a, b + 1) or a in range(min_cover, max_cover + 1) or b in range(min_cover, max_cover + 1)):
isValid = False
break
return isValid
The algorithm begins by checking the first len(C) + 1 / 2 nails in C:
First it calculates the smallest and largest value that the nails in this range can cover (min_cover and max_cover).
Next, it looks through A & B, and checks whether each plank can be nailed by any of the nails in the range (min_cover, max_cover).
If the result is False, we update min_nails to be mid_nails + 1 and repeat. If the result is True, we store the number of nails in the valid array, and attempt to find a smaller amount of nails which would also work, by setting max_nails = mid_nails - 1
This approach scores 100% correctness, however fails on the performance tests because it produces incorrect results - for each of the performance tests, the minimum number of nails obtained is much lower than the expected result. I suspect the error would be in this line: if not(min_cover in range(a, b + 1) or max_cover in range(a, b + 1) or a in range(min_cover, max_cover + 1) or b in range(min_cover, max_cover + 1))
but I can't figure out what it is.

The problem with your if condition can be seen with this sample input:
A = [1,3,5]
B = [2,4,6]
C = [1,5,3]
print(solution(A, B, C))
This will print 2, but the expected output is 3, as all three nails are needed.
Yet, your code will have is_valid_nails_amount(2, A, B, C) return True, despite the fact that the second plank is not nailed by those two nails.
The problem is that neither of these conditions guarantees that a nail hits the plank (a, b):
a in range(min_cover, max_cover + 1)
b in range(min_cover, max_cover + 1)
Your algorithm really needs to check if there is a nail in that min/max range that is available for hitting the plank.

Related

Auto-Decryption Question from Computer Science Circles (Python)

I'm doing this exercise from computer science circle. Here's the information:
Write a program which does the following. First, it should read one line of input, which is the encoded message, and will consist of capital letters and spaces.
Your program must try decoding the message with all 26 possible values of the shift S; out of these 26 possible original messages, print the one which has the highest goodness.
For your convenience, we will pre-define the variable letterGoodness for you, a list of length 26 which equals the values in the frequency table above,
letterGoodness = [.0817,.0149,.0278,.0425,.1270,.0223,.0202,...
I've done some coding but it kept saying 'list index out of range' at line 24 (the letterGoodness[diff] line). I checked but couldn't find out why.
Here's the code:
#subtask 1: shifting values
a = input ()
def Decode(S):
b = ""
for i in range (0, len (a)):
if a[i] != " ":
if ord(a[i]) + int(S) <= 90:
c = chr(ord(a[i]) + int(S))
b = b + c
else:
c = chr(ord(a[i]) + int(S) - 65)
b = b + c
else:
b = b + a[i]
return b
#subtask 2: determining goodness
goodness = 0
maxx = 0
for S in range (1, 27):
b = Decode (S)
for ii in range (0, len(b)):
if b[ii] != " ":
diff = (ord(b[ii]) - 65)
goodness = goodness + letterGoodness[diff]
if goodness > maxx:
maxx = goodness
out = b
print (out)

String of numbers converted to int and added to a list(Optimization issue)

I've managed to make the code work but i believe it could be optimized... a lot.
The input is a string of numbers separated with spaces. Something like - 4 2 8 6 or 1 2 3 4 5 6 7
It has to find which 3 numbers match this condition a + b == c. While 'b' is always on the right side of 'a' and for every time the condition is met print the numbers on the console in the following format - 'a + b == c'. If there isn't a single match print 'No'.
The only restriction is for 'b' to be at least 1 index away from 'a'.
This is what I have come up with.
lineOfNums = input('Line of numbers: ') # User input: example - 4 2 6 8
arrNums = lineOfNums.split()
conMet = False # Is the condition met at least once
for a in range(0, len(arrNums)):
for b in range(a + 1, len(arrNums)):
for c in range(0, len(arrNums)):
if int(arrNums[a]) + int(arrNums[b]) == int(arrNums[c]):
print(f'{arrNums[a]} + {arrNums[b]} == {arrNums[c]}')
conMet = True
if conMet == False: print('No')
You can do it with itertools, first of course convert to int
from itertools import combinations
# Convert to int
arr= [int(i) for i in arrNums]
# Get all the combinations
psums = {sum(i): i for i in combinations(arr, 2)}
# Then loop once
for i, v in enumerate(arr):
if v in psums:
print(f'{psums[v][0]} + {psums[v][1]} == {v}')
The big O for this algorithm is O(n^2) on average, which comes from O(n choose r), where n is the number of inputs (4 in this example) and r is the count of numbers your summing, in this case 2.
First, do the integer conversion once when you create arrNum, not every time through the loops.
arrNum = [int(x) for x in lineOfNums.split()]
The outer loop only needs to go to len(arrNums)-1, since it needs to leave room for B to the right of it.
for a in range(0, len(arrNums)-1):
for b in range(a + 1, len(arrNums)):
for c in range(0, len(arrNums)):
if arrNums[a] + arrNums[b] == arrNums[c]:
print(f'{arrNums[a]} + {arrNums[b]} == {arrNums[c]}')
conMet = True

How to make this breadth first search faster?

I have a search algorithm that looks for combinations of add and multiply functions to reach a certain range of number from a certain range of numbers. It is searching for the shortest program, a program being a something like AAMMA where the initial number is added, added, multiplied, multiplied, add where the ending number is in the range r to s. It has to work for every number in the starting range p to q.
The input is a and m, what you are adding and multiplying by(num+a), (num*m) for each function. What I am doing is trying every combination of functions until I find one that works, stopping that branch if it gets too big. If I find "program" that works I try the program on all of the other numbers in the starting range. It does this until either it finds no branches that don't reach the range without going over.
I know the search isn't super typical, but I don't think there is a possibility for duplicates so I didn't include a found list.
It works for smaller ranges and inputs like
Problem3("1 2 2 3 10 20")
but for larger ranges, it just takes forever my test case is
Problem3("8 13 28 91 375383947 679472915")
which I haven't even seen complete. What is my best approach from here, multithreading(hope not), making my inner functions faster somehow or just scraping this approach.
def Problem3(s):
a,m,p,q,r,s = list(map(int, s.split(" ")))
print(str(a) + "-C-" + str(m) + " processor")
print("Input guarenteed between " + str(p) + " and " + str(q))
print("Output is real number between " + str(r) + " and " + str(s))
open_set = queue.Queue()
# curr path depth
open_set.put([p, "", 0])
while not open_set.empty():
subroot = open_set.get()
multiCurr = subroot[0] * m
addCurr = subroot[0] + a
depth = subroot[2] + 1
if r <= addCurr <= s:
truePath = True
#If we find a working path, we need to check if it works for the other things
path = subroot[1] + "A"
for x in range(p, q+1):
for op in path:
if op == "A":
x += a
if op == "M":
x *= m
if r <= x <= s:
pass
else:
truePath = False
break
if truePath:
print("Found " + path + " at depth " + str(depth) + " with starting number " + str(p) + ", output " + str())
if r <= multiCurr <= s:
truePath = True
path = subroot[1] + "M"
for x in range(p, q+1):
for op in path:
if op == "A":
x += a
if op == "M":
x *= m
if r <= x <= s:
pass
else:
truePath = False
break
if truePath:
print("Found " + path + " at depth " + str(depth) + " with starting number " + str(p) + ", output " + str())
if addCurr > s and multiCurr > s:
pass
elif multiCurr > s:
open_set.put([addCurr, subroot[1] + "A", depth])
elif addCurr > s:
open_set.put([multiCurr, subroot[1] + "M", depth])
else:
open_set.put([multiCurr, subroot[1] + "M", depth])
open_set.put([addCurr, subroot[1] + "A", depth])
You don't need to test every value in the range(p, q + 1) sequence. You only need to test for p and q. If it works for the lowest and the highest number, it'll work for all the values in between, because the problem has been reduced to just multiplication and addition. You really only need to test the progress of program(q), keeping it below s, until you have created the shortest program that puts program(p) at or above r.
However, this isn't really a great problem for breath-first search; your second example would require testing 17.6 trillion possible states; the shortest solution is 44 characters long, so a breath-first search would explore 2 ** 44 states, so 17,592,186,044,416 to be exact! Even using a compiled programming language like C would take a long, long time to find the solution using such a search. Instead, you can just generate the string using a bit of math.
You can calculate the maximum number of multiplications needed here with int(math.log(s // q, m)), that's the number of times you can multiply with m when starting at q and still stay below s. You can't ever use more multiplications! With math.ceil(math.log(r / p, m)) you can find the minimum number of multiplications that would put p at or above r. To minimise the program length, pick the lower value of those two numbers.
Then, start fitting in A additions, before each M multiplication. Do so by taking i as the number of M characters that are to follow, then dividing both r and s by m ** i. These inform the number a additions to p and q that together with the subsequent multiplications bring it closest to r and s; the difference with the current p and q let you calculate the minimum number of A characters you can insert here to keep within the [r, s] range. For p, round up, for q, round down.
Repeat this procedure for every subsequent M operation, updating the p and q values with the results each time:
import math
def problem3(s):
a, m, p, q, r, s = map(int, s.split())
p_mult = math.ceil(math.log(math.ceil(r / p), m))
q_mult = int(math.log(s // q, m))
mult = min(p_mult, q_mult)
program = []
for i in range(mult, -1, -1):
p_additions = math.ceil((math.ceil(r / (m ** i)) - p) / a)
q_additions = ((s // (m ** i)) - q) // a
additions = min(p_additions, q_additions)
program += [additions * 'A']
if i:
p, q = (p + (additions * a)) * m, (q + (additions * a)) * m
program += ['M']
return ''.join(program)
This is a closed-form solution, no search needed. The result is guaranteed to be the shortest:
>>> problem3("1 2 2 3 10 20")
'AMM'
>>> problem3("8 13 28 91 375383947 679472915")
'AAAAAAMAAMAAAAAAAAAAAMAAAAAMAAAMAAAAMAAAAAAA'

What's wrong with my Extended Euclidean Algorithm (python)?

My algorithm to find the HCF of two numbers, with displayed justification in the form r = a*aqr + b*bqr, is only partially working, even though I'm pretty sure that I have entered all the correct formulae - basically, it can and will find the HCF, but I am also trying to provide a demonstration of Bezout's Lemma, so I need to display the aforementioned displayed justification. The program:
# twonumbers.py
inp = 0
a = 0
b = 0
mul = 0
s = 1
r = 1
q = 0
res = 0
aqc = 1
bqc = 0
aqd = 0
bqd = 1
aqr = 0
bqr = 0
res = 0
temp = 0
fin_hcf = 0
fin_lcd = 0
seq = []
inp = input('Please enter the first number, "a":\n')
a = inp
inp = input('Please enter the second number, "b":\n')
b = inp
mul = a * b # Will come in handy later!
if a < b:
print 'As you have entered the first number as smaller than the second, the program will swap a and b before proceeding.'
temp = a
a = b
b = temp
else:
print 'As the inputted value a is larger than or equal to b, the program has not swapped the values a and b.'
print 'Thank you. The program will now compute the HCF and simultaneously demonstrate Bezout\'s Lemma.'
print `a`+' = ('+`aqc`+' x '+`a`+') + ('+`bqc`+' x '+`b`+').'
print `b`+' = ('+`aqd`+' x '+`a`+') + ('+`bqd`+' x '+`b`+').'
seq.append(a)
seq.append(b)
c = a
d = b
while r != 0:
if s != 1:
c = seq[s-1]
d = seq[s]
res = divmod(c,d)
q = res[0]
r = res[1]
aqr = aqc - (q * aqd)#These two lines are the main part of the justification
bqr = bqc - (q * aqd)#-/
print `r`+' = ('+`aqr`+' x '+`a`+') + ('+`bqr`+' x '+`b`+').'
aqd = aqr
bqd = bqr
aqc = aqd
bqc = bqd
s = s + 1
seq.append(r)
fin_hcf = seq[-2] # Finally, the HCF.
fin_lcd = mul / fin_hcf
print 'Using Euclid\'s Algorithm, we have now found the HCF of '+`a`+' and '+`b`+': it is '+`fin_hcf`+'.'
print 'We can now also find the LCD (LCM) of '+`a`+' and '+`b`+' using the following method:'
print `a`+' x '+`b`+' = '+`mul`+';'
print `mul`+' / '+`fin_hcf`+' (the HCF) = '+`fin_lcd`+'.'
print 'So, to conclude, the HCF of '+`a`+' and '+`b`+' is '+`fin_hcf`+' and the LCD (LCM) of '+`a`+' and '+`b`+' is '+`fin_lcd`+'.'
I would greatly appreciate it if you could help me to find out what is going wrong with this.
Hmm, your program is rather verbose and hence hard to read. For example, you don't need to initialise lots of those variables in the first few lines. And there is no need to assign to the inp variable and then copy that into a and then b. And you don't use the seq list or the s variable at all.
Anyway that's not the problem. There are two bugs. I think that if you had compared the printed intermediate answers to a hand-worked example you should have found the problems.
The first problem is that you have a typo in the second line here:
aqr = aqc - (q * aqd)#These two lines are the main part of the justification
bqr = bqc - (q * aqd)#-/
in the second line, aqd should be bqd
The second problem is that in this bit of code
aqd = aqr
bqd = bqr
aqc = aqd
bqc = bqd
you make aqd be aqr and then aqc be aqd. So aqc and aqd end up the same. Whereas you actually want the assignments in the other order:
aqc = aqd
bqc = bqd
aqd = aqr
bqd = bqr
Then the code works. But I would prefer to see it written more like this which is I think a lot clearer. I have left out the prints but I'm sure you can add them back:
a = input('Please enter the first number, "a":\n')
b = input('Please enter the second number, "b":\n')
if a < b:
a,b = b,a
r1,r2 = a,b
s1,s2 = 1,0
t1,t2 = 0,1
while r2 > 0:
q,r = divmod(r1,r2)
r1,r2 = r2,r
s1,s2 = s2,s1 - q * s2
t1,t2 = t2,t1 - q * t2
print r1,s1,t1
Finally, it might be worth looking at a recursive version which expresses the structure of the solution even more clearly, I think.
Hope this helps.
Here is a simple version of Bezout's identity; given a and b, it returns x, y, and g = gcd(a, b):
function bezout(a, b)
if b == 0
return 1, 0, a
else
q, r := divide(a, b)
x, y, g := bezout(b, r)
return y, x - q * y, g
The divide function returns both the quotient and remainder.
The python program that does what you want (please note that extended Euclid algorithm gives only one pair of Bezout coefficients) might be:
import sys
def egcd(a, b):
if a == 0:
return (b, 0, 1)
g, y, x = egcd(b % a, a)
return (g, x - (b // a) * y, y)
def main():
if len(sys.argv) != 3:
's program caluclates LCF, LCM and Bezout identity of two integers
usage %s a b''' % (sys.argv[0], sys.argv[0])
sys.exit(1)
a = int(sys.argv[1])
b = int(sys.argv[2])
g, x, y = egcd(a, b)
print 'HCF =', g
print 'LCM =', a*b/g
print 'Bezout identity: %i * (%i) + %i * (%i) = %i' % (a, x, b, y, g)
main()

leading number groups between two numbers

(Python) Given two numbers A and B. I need to find all nested "groups" of numbers:
range(2169800, 2171194)
leading numbers: 21698XX, 21699XX, 2170XX, 21710XX, 217110X, 217111X,
217112X, 217113X, 217114X, 217115X, 217116X, 217117X, 217118X, 2171190X,
2171191X, 2171192X, 2171193X, 2171194X
or like this:
range(1000, 1452)
leading numbers: 10XX, 11XX, 12XX, 13XX, 140X, 141X, 142X, 143X,
144X, 1450, 1451, 1452
Harder than it first looked - pretty sure this is solid and will handle most boundary conditions. :) (There are few!!)
def leading(a, b):
# generate digit pairs a=123, b=456 -> [(1, 4), (2, 5), (3, 6)]
zip_digits = zip(str(a), str(b))
zip_digits = map(lambda (x,y):(int(x), int(y)), zip_digits)
# this ignores problems where the last matching digits are 0 and 9
# leading (12000, 12999) is same as leading(12, 12)
while(zip_digits[-1] == (0,9)):
zip_digits.pop()
# start recursion
return compute_leading(zip_digits)
def compute_leading(zip_digits):
if(len(zip_digits) == 1): # 1 digit case is simple!! :)
(a,b) = zip_digits.pop()
return range(a, b+1)
#now we partition the problem
# given leading(123,456) we decompose this into 3 problems
# lows -> leading(123,129)
# middle -> leading(130,449) which we can recurse to leading(13,44)
# highs -> leading(450,456)
last_digits = zip_digits.pop()
low_prefix = reduce(lambda x, y : 10 * x + y, [tup[0] for tup in zip_digits]) * 10 # base for lows e.g. 120
high_prefix = reduce(lambda x, y : 10 * x + y, [tup[1] for tup in zip_digits]) * 10 # base for highs e.g. 450
lows = range(low_prefix + last_digits[0], low_prefix + 10)
highs = range(high_prefix + 0, high_prefix + last_digits[1] + 1)
#check for boundary cases where lows or highs have all ten digits
(a,b) = zip_digits.pop() # pop last digits of middle so they can be adjusted
if len(lows) == 10:
lows = []
else:
a = a + 1
if len(highs) == 10:
highs = []
else:
b = b - 1
zip_digits.append((a,b)) # push back last digits of middle after adjustments
return lows + compute_leading(zip_digits) + highs # and recurse - woohoo!!
print leading(199,411)
print leading(2169800, 2171194)
print leading(1000, 1452)
def foo(start, end):
index = 0
is_lower = False
while index < len(start):
if is_lower and start[index] == '0':
break
if not is_lower and start[index] < end[index]:
first_lower = index
is_lower = True
index += 1
return index-1, first_lower
start = '2169800'
end = '2171194'
result = []
while int(start) < int(end):
index, first_lower = foo(start, end)
range_end = index > first_lower and 10 or int(end[first_lower])
for x in range(int(start[index]), range_end):
result.append(start[:index] + str(x) + 'X'*(len(start)-index-1))
if range_end == 10:
start = str(int(start[:index])+1)+'0'+start[index+1:]
else:
start = start[:index] + str(range_end) + start[index+1:]
result.append(end)
print "Leading numbers:"
print result
I test the examples you've given, it is right. Hope this will help you
This should give you a good starting point :
def leading(start, end):
leading = []
hundreds = start // 100
while (end - hundreds * 100) > 100:
i = hundreds * 100
leading.append(range(i,i+100))
hundreds += 1
c = hundreds * 100
tens = 1
while (end - c - tens * 10) > 10:
i = c + tens * 10
leading.append(range(i, i + 10))
tens += 1
c += tens * 10
ones = 1
while (end - c - ones) > 0:
i = c + ones
leading.append(i)
ones += 1
leading.append(end)
return leading
Ok, the whole could be one loop-level deeper. But I thought it might be clearer this way. Hope, this helps you...
Update :
Now I see what you want. Furthermore, maria's code doesn't seem to be working for me. (Sorry...)
So please consider the following code :
def leading(start, end):
depth = 2
while 10 ** depth > end : depth -=1
leading = []
const = 0
coeff = start // 10 ** depth
while depth >= 0:
while (end - const - coeff * 10 ** depth) >= 10 ** depth:
leading.append(str(const / 10 ** depth + coeff) + "X" * depth)
coeff += 1
const += coeff * 10 ** depth
coeff = 0
depth -= 1
leading.append(end)
return leading
print leading(199,411)
print leading(2169800, 2171194)
print leading(1000, 1453)
print leading(1,12)
Now, let me try to explain the approach here.
The algorithm will try to find "end" starting from value "start" and check whether "end" is in the next 10^2 (which is 100 in this case). If it fails, it will make a leap of 10^2 until it succeeds. When it succeeds it will go one depth level lower. That is, it will make leaps one order of magnitude smaller. And loop that way until the depth is equal to zero (= leaps of 10^0 = 1). The algorithm stops when it reaches the "end" value.
You may also notice that I have the implemented the wrapping loop I mentioned so it is now possible to define the starting depth (or leap size) in a variable.
The first while loop makes sure the first leap does not overshoot the "end" value.
If you have any questions, just feel free to ask.

Categories

Resources