Binary Search Guessing Game - python

I am trying to create a small program that uses binary search algorithm to have the computer itself guess a number given a set of arguments.
The arguments the function takes in are how many 'tries' it has to guess and the values that it can guess between (for example, 5 tries to guess the number in between 0 and 10).
When I run my code, it only seems to run my else: statement, regardless of the arguments I pass in.
I am missing something in my "if" statements but I am stumped and can't figure out what I have done incorrectly.
Thank you so much for your time and help!
import random
def guess_random_number_binary(tries, start, stop):
rand_number = random.randint(start,stop)
num_list = range(start,stop)
lower_bound = start
upper_bound = len(str(stop)) - 1
while lower_bound <= upper_bound:
pivot = (lower_bound + upper_bound) // 2
pivot_value = num_list[pivot]
if pivot_value == rand_number and tries > 0:
print("Found it! " + str(rand_number))
return pivot
if pivot_value > rand_number and tries > 0:
upper_bound = pivot - 1
tries -= 1
print ("incorrect guess" + str(tries) + " Remaining")
else:
lower_bound = pivot + 1
print("Out of tries")
return
guess_random_number_binary(5, 0 ,10)
UPDATE:
import random
def guess_random_number_binary(tries, start, stop):
rand_number = random.randint(start,stop)
num_list = []
lower_bound = start
upper_bound = stop
num_list = range(start,stop+1)
while lower_bound <= upper_bound:
pivot = (lower_bound + upper_bound) // 2
pivot_value = num_list[pivot]
if tries > 0:
if pivot_value == rand_number:
print("Found it! " + str(rand_number))
return pivot
elif pivot_value > rand_number:
upper_bound = pivot - 1
tries -= 1
print ("Guessed " + str(pivot) + " incorrectly \n" + str(tries) + " Tries remaining")
elif pivot_value < rand_number:
lower_bound = pivot + 1
tries -= 1
print ("Guessed " + str(pivot) + " incorrectly \n" + str(tries) + " Tries remaining")
else:
print ("Ran out of tries!")
break
guess_random_number_binary(5, 20 ,30)
I have been trying to debug and even if my new code is over simplified, I hope that it is at least headed in the right direction.
I believe the main issue stands with how I am creating "num_list" as pointed out by an answer below. Receiving an IndexError, which makes sense in theory. However, I cannot seem to find an alternative for creating that list.
Once again, thank you.

You're resetting the value of pivot at the beginning of each loop, so
You passed in the value 10 for stop. So in the line upper_bound = len(str(stop)) - 1,
upper_bound = len(str(stop))-1 = len(str(10) = len ('10')-1 = 1.
Your while loop never runs, because lower_bound is never less than upper_bound.
You perhaps were at some point intending to do upper_bound = len(num_list)-1 and somehow wrote upper_bound = len(str(stop)) - 1 instead. However, even that would not have been correct; the length of range(start,stop) is stop-start, not stop-start+1 (range is not inclusive of stop). And using range in the first place is unnecessarily confusing and would not work for larger start values. For instance, suppose (start, stop) = (20, 30). Then pivot will be 25. But the range object will have only 10 elements, so num_range[pivot] will return an error.

Related

My for loop randomly gets stuck and doesnt complete its range

My for loop keeps stopping in main. It is supposed to call the def binarySearch 10000 times and add up the total number of guesses from each try. I notice when I lower the range down to 100, the for loop works MOST of the time. It still sometimes just freezes and never prints anything.
import random
def binarySearch(left, right, x):
counter = 0 #guesses per try
while left <= right:
counter+=1
mid = (right + left) // 2
if x == mid:
return counter
elif x > mid:
left = mid
elif x < mid:
right = mid
def main():
guesses = 0 # total number of guesses
###Problem here###
for x in range(10001):
lef = 0 #min
rig = 1000 #max
num = random.randint(lef, rig) #random number
guesses += binarySearch(lef, rig, num) #calls binarySearch and sums the total guesses
print("Total guesses from 10000 tries: ", guesses)
main()
EDIT:
I narrowed down the problem to:
elif x < mid:
left = mid
I tested it quite a few times and came to the conclusion that the while loop ends up getting stuck repeating that else if statement. I do not know why this is happening though.
The reason it is getting stuck is because there is an error in the boundary condition. If the number is not equal to mid and the left and right should be equal to mid + 1 and mid - 1 respectively. No need to consider mid again. Since you are considering mid again and again, your condition is never coming out of this cycle and hence the infinite loop.
elif x > mid:
left = mid + 1
elif x < mid:
right = mid - 1
These should be your new values for left and right. No need to consider mid again because it is not equal to the target value, if it were, the top most if the condition would have been true.

How to determine the complexity of python code?

I need to understand the complexity of the following code. I am familiar with the concepts of all the Big O() notation and also have read a lot of blogs but I cant figure how to apply to big programs.
Following is the code for largest pallindrome number from 100 to 999:
def isPaindrome(number):
stringNum = str(number)
firstDigit_index,lastDigit_index=0,len(stringNum)-1
isPalindrome = False
while(lastDigit_index > 0 and firstDigit_index < lastDigit_index):
#print(stringNum[f],"==",stringNum[l],"......")
if(stringNum[firstDigit_index]==stringNum[lastDigit_index]):
isPalindrome = True
else:
isPalindrome = False
break
firstDigit_index = firstDigit_index + 1
lastDigit_index = lastDigit_index - 1
if(isPalindrome):
return number
else:
return 0
max = 0
startRange = 100
endRange = 999
for i in range(endRange*endRange,startRange*startRange,-1):
factors = []
result = isPaindrome(i)
if(result!=0):
for i in range(startRange,endRange+1):
if(result%i==0):
factors.append(i)
if(len(factors)>1):
sumFactor = factors[(len(factors))-1] + factors[(len(factors))-2]
mul = factors[(len(factors))-1] * factors[(len(factors))-2]
if(sumFactor>max and mul==result):
max = sumFactor
print("Largest Palindrome made from product of two 3 digit numbers(",factors[(len(factors))-1],",",factors[(len(factors))-2] ,") is", result,".")
If anyone could just make me understant step by step how to calculate I'd be grateful.
As I mentioned, your isPalindrome function is literally incorrect, as you're not changing the indexes. I changed a bit of your also, so this is the version I'm analysing. (I'm only analysing the isPalindrome function, since I actually couldn't understand what the main function is doing), sorry!
def isPalindrome(n):
num = str(n)
head, tail = 0, len(num) - 1
while tail > head:
if num[head] != num[tail]: # Not symmetrical!! WARNING!
return False
head += 1 # move position
tail -= 1 # move position
return True
This code on average is O(|n|) i.e. O(log N) where N is the number. This is because on average, the if comparison has 50% chance of breaking (returning False) and 50% of continuing. Therefore the expected number of comparisons would be |n|/4 which is O(|n|).

Bisection Search in Python - Find lowest payment over one year

I've been going nuts about this problem for hours, and I've been redoing it over and over! At this point I think I'm actually seeing numbers flying around me.
Anyway, I'm supposed to write a program that finds the correct amount of money to pay each month over one year, to pay off debt on a credit card. So with this program, there's a few conditions that must be met.
It must be done by using bisection search ((low + high) / 2)
There's a set balance
There's an annual interest rate.
Here's my code at the moment, and all I'm getting with this is infinite loops. What am I doing wrong here?
balance = 320000
annualInterestRate = 0.2
monthly_interest = float(annualInterestRate) / 12.0
lower_bound = float(balance / 12)
upper_bound = (balance * (1 + monthly_interest)**12) / 12.0
epsilon = 0.01
ans = float(lower_bound + upper_bound) / 2
while abs((balance * (1 + monthly_interest)**12) / 12.0) >= epsilon:
ans = float(lower_bound + upper_bound) / 2
total = float(ans * 12)
new_balance = 0
interest = 0
for month in range(0, 12):
interest += ans + (1 + monthly_interest)
new_balance += ans + interest
if new_balance > balance:
upper_bound = ans
print "low" + str(new_balance)
elif new_balance < balance:
lower_bound = ans
print "high" + str(new_balance)
else:
print "What's going on here?"
print "Lowest payment: %r" % ans
I believe there are a couple things wrong here, so first things first, your while is an infinite loop because the condition you are using will never converge to a solution (the variable values never change inside the loop). On top of that the condition (of the while) seems wrong for this kind of problem.
This is what I think you are trying to do, you are trying to find the upper and lower bounds for "the monthly payment" and the convergence condition for that is that the difference between those bounds should be less to a constant epsilon (in other words the error should be less than epsilon).
inside your loop you are calculating the midpoint correctly, this midpoint already is taking into account the interest but are calculating it again. The conditions to change the upper and lower bound are not taking into account the interest so this part of the code is a little messy.
So, modifying those conditions your program actually converges to a solution:
balance = 320000
annualInterestRate = 0.2
monthly_interest = float(annualInterestRate) / 12.0
lower_bound = float(balance / 12)
upper_bound = (balance * (2 + monthly_interest)**12) / 12.0
epsilon = 0.001
ans = float(lower_bound + upper_bound) / 2
total_debt=balance * (1 + annualInterestRate)
print total_debt
while (upper_bound - lower_bound) >= epsilon:
ans = float(lower_bound + upper_bound) / 2
total = float(ans * 12)
if total > total_debt:
upper_bound = ans
print "low " + str(total)
elif total < total_debt:
lower_bound = ans
print "high " + str(total)
else:
print "Solution found"
break
print "Lowest payment: %r" % ans
Hope it helps!

def getLotteryGame(): won't return(True)

In my def getLotteryGame(): Which is suppose to check if the timer runs out and if it does it sorts the players, ranks them, gives them their winnings and stores a note for them and then return True and when it returns True the bot reloads the game making a new round. I tried several ways of trying to get it to return True. This is the code:
def getLotteryGame():
global pot
global players
different = float(time.time() - lotteryStart)
years = int(different / Point.YEAR)
days = int((different % Point.YEAR) / Point.DAY)
hours = int((different % Point.DAY) / Point.HOUR)
mins = int((different % Point.HOUR) / Point.MINUTE)
secs = int(different % Point.MINUTE)
if secs <= 0:
if len(players) > 0:
random.shuffle(players)
ratios = []
for i in range(-2, len(players) - 2) if i > 0 else range(len(players)):
if i < 0:
ratios.append(1 - (i * 0.33)) # ratio > 1
else:
ratios.append(1 / (1 + (i * 0.33))) # ratio <= 1
winnings = [pot * r for r in ratios]
for m in range(1, len(players)):
notes.store("~lottery~", players[m], "The system has placed you "+Point.ordinal(m)+" in the lottery. The lottery awarded you "+winnings+" P$", time.time())
alerts.append(players[m])
winnings = int(winnings)
point = Point.dPoint[players[m]]
point = int(point)
point = int(point+winnings)
Point.dPoint[players[m]] = int(point)
return(True)
elif len(players) == 0:
return(True)
else:
return(False)
When I wait for the difference to go <= 0 for if secs <= 0. It keeps returning False instead and I'm not sure why.
Your code logic is brokem. lotteryStart is defined when the code first runs, as time.time(). Later, you find the number of seconds since lotteryStart. This number of seconds, might be zero occasionally, and is never going to be less than zero. Since time moves forward, it should always be positive. Thus your code always executes the final else statement that returns False.
different = float(time.time() - lotteryStart)
Maybe lotteryStart is a global variable as well as it is not defined anywhere?

Wrong Answer to SPOJ PRIME1 in Python

I've tried to compare my result to Wolfram Alpha's result by counting primes found.
It seems works well, without error.
But when I submit this solution to SPOJ, it shows the error message "wrong answer".
I also tried to change the final print's end= to '' (blank string), but still got "wrong answer".
Not sure if is there something wrong with my sieve algorithm, or output error.
**edit: link of problem http://www.spoj.pl/problems/PRIME1/
Here is my PRIME1 source code, hope someone can point out my fault. Thanks a lot.
And hope someone can teach me how to write a test to programs like this, I'm still learning, don't know how to do automated tests to programs, but want to learn.
def getPrimeInRange(minNum, maxNum):
#just a variation with skipping step of the Sieve of E's
processingRange = list(range(minNum, maxNum+1))
#prefix, due to 1 is not a prime
if minNum == 1:
processingRange[0] = 0
sqrtOfMaxNum = int(maxNum ** 0.5) + 1
primesUnderSqrt = list(range(sqrtOfMaxNum))
#prefix, due to 1 is not a prime
primesUnderSqrt[1] = 0
#my strategy is flip all numbers that is not a prime to zero, which equals to False.
#for Sieve of E's, all the primes under sqrt of max num are the only needed numbers to sieve primes out.
#so here is a smaller Sieve of E's for numbers under sqrt
for n in primesUnderSqrt:
if n: #which equals to "if n != 0"
nowIndex = n + n
while True:
try:
primesUnderSqrt[nowIndex] = 0
nowIndex += n
except IndexError:
break
#full aspect sieve
for n in primesUnderSqrt:
if n:
#for easier flip processing, I need the offset number for the flipping.
nMultipleMostCloseToMin = n * (minNum // n)
if nMultipleMostCloseToMin == minNum:
nowIndex = 0
elif sqrtOfMaxNum <= minNum:
nowIndex = nMultipleMostCloseToMin + n - minNum
elif sqrtOfMaxNum > minNum:
nowIndex = nMultipleMostCloseToMin + n - minNum + n
#happy flippin'
while True:
try:
processingRange[nowIndex] = 0
nowIndex += n
except IndexError:
break
return processingRange
def main():
todoTimes = int(input())
todoNums = list(range(todoTimes))
stringOutput = ''
for i in range(todoTimes):
todoNums[i] = input().split()
todoNums[i][0] = int(todoNums[i][0])
todoNums[i][1] = int(todoNums[i][1])
for [minNum, maxNum] in todoNums:
#countedNum = 0 #for algo debugging
for n in getPrimeInRange(minNum, maxNum):
if n:
stringOutput += str(n)
#countedNum += 1 #for algo debugging
stringOutput += '\n'
stringOutput += '\n'
#stringOutput += str(countedNum) #for algo debugging
stringOutput = stringOutput.rstrip('\n')
print(stringOutput)
ifMainSucceed = main()
This part of your logic
if nMultipleMostCloseToMin == minNum:
nowIndex = 0
elif sqrtOfMaxNum <= minNum:
nowIndex = nMultipleMostCloseToMin + n - minNum
elif sqrtOfMaxNum > minNum:
nowIndex = nMultipleMostCloseToMin + n - minNum + n
is wrong. Your elif-conditions don't make much sense here. If n is not a divisor of minNum, the smallest multiple of n not less than minNum is nMultipleMostCloseToMin + n, regardless of whether sqrtOfMaxNum is larger than minNum or not. The condition you intended here was n <= minNum, to avoid crossing off the prime itself.

Categories

Resources