Binary search program - python

def bsearch(s, e, first, last):
print(first, last)
if (last - first) < 2:
return s[first] == e or s[last] == e
mid = first + (last - first) / 2
if s[mid] == e: return True
if s[mid] > e: return bsearch(s, e, first, mid - 1)
return bsearch(s, e, mid + 1, last)
def search1(s,e):
print(bsearch(s, e, 0, len(s)-1))
print('Search complete')
def testSearch():
s = range(0, 1000000)
input('binary,-1')
print(search1(s, -1))
It's binary search algorithm. I have two questions.
Question 1:
Why is first necessary in the following line?
mid = first + (last - first) / 2
Question 2:
I can't run the result, when I ran the program.
The error message is:
range indices must be integers or slices, not float.
How I can solve it?

Starting with Question 2:
The error message "range indices must be integers or slices, not float" tells you that you are trying to slice (get a part of a list-like collection, e.g. with some_list[5:]) using float, not int. To be more precise, your mid is 5.0, not 5.
In Python 3 the default division is floating division (unlike Python 2). To get the integer division you need to use double slash operator:
mid = first + (last - first) // 2
Regarding Question 1: mid is the middle element. It is elementary equation to get the middle element, but maybe you are confused because you expected the other, equivalent equation; it can be calculated as
mid = first + (last - first) // 2
or:
mid = (first + last) // 2
The equation could be different if you were using an implementation which creates copies of list s at each recursive call. The implementation you provided works "in-place" so it needs the absolute coordinates.
EDIT: as pointed by #philipxy in comments, you could easily find what was causing the slice-related error, searching for your error message + "division".
I just tested this and the first Google result points to TypeError: list indices must be integers, not float; as this is exactly the same problem as yours (binary search), this question could be flagged as duplicate and closed if not your second question contained herein. In fact some more experienced users still may decide do flag it as a duplicate.

Related

Python recursion (format issue)

Write a recursive function replace_digit(n, d, r) which replaces each occurrence of digit d in the number n by r.
replace_digit(31242154125, 1, 0) => 30242054025
My code is as such
def replace_digit(n, d, r):
y=str(n)
if len(y)==0:
return ''
else:
if y[0]== str(d):
return str(r) + replace_digit(str(n)[1:],d,r)
else:
return y[0]+ replace_digit(str(n)[1:],d,r)
However, the answer I get is in a string format. Any idea how to convert into an integer format? I have been stuck for quite some time on this :(
If your recursive function must return an integer, then return integers. You can always convert the returned integer back into a string for recursive calls.
You'll have to stop when you run out of digits before calling, so only recurse if there are 2 or more characters in y.
However, this approach a big problem: leading zeros are dropped when converting to int():
>>> int('025')
25
You have two options here:
Pad the number when you convert to a string (using str.zfill() or format(), and use the length of the value you passed into the recursive call).
Recurse from the end. This would also allow you to not use strings.
Here is an approach using zero-padding:
def replace_digit(n, d, r):
nstr = str(n)
first, rest = nstr[0], nstr[1:]
if rest:
rest = str(replace_digit(rest, d, r)).zfill(len(rest))
if first == str(d):
first = str(r)
return int(first + rest)
Note that you always want to separate out the first character from the tail anyway, so I used variables for both.
This way, you can use if rest: to guard against recursing when there are no digits left, and you can call str() on the return value. The function returns the int() conversion of the (possibly replaced first value) with the updated rest value.
Demo:
>>> replace_digit(31242154125, 1, 0)
30242054025
Recursing from the opposite end would not have problems with zeros, except if the input value was 0 to begin with. However, you could instead use division and modules operations to work on the integer value directly:
number % 10 gives you the right-most digit, as an integer.
number // 10 gives you the remaining numbers, again as integer.
You could combine the two operations into one using the divmod() function. Personally, I don't do so, as I don't think it particularly improves readability, and using the operators is slightly faster when using CPython.
You can re-combine the recursive call result with the (possibly replaced) last digit by multiplying the returned value by 10 again:
def replace_digit(n, d, r):
head, last = n // 10, n % 10
if head:
head = replace_digit(head, d, r)
if last == d:
last = r
return (head * 10) + last
This works for any natural number, including 0:
>>> replace_digit(0, 1, 0)
0
>>> replace_digit(0, 0, 1)
1
>>> replace_digit(31242154125, 1, 0)
30242054025
>>> replace_digit(31242154125, 4, 9)
31292159125

Display the middle elements of a string

Recently i tried learning to program and after finishing my first tutorial I am trying tackling some problems from codewars.com.
"You are going to be given a word. Your job is to return the middle character of the word. If the word's length is odd, return the middle character. If the word's length is even, return the middle 2 characters."
Here is my solution:
def get_middle(n):
if len(n) % 2 == 0:
return n[(len(n)/2) - 1] and n[(len(n)/2)]
else:
return n[(len(n)/2) + 0.5]
Unfortunately when executing the function with for example "abc" I always get:
Traceback (most recent call last) <ipython-input-24-46429b2608e5> in <module>
----> 1 print(get_middle("abc"))
<ipython-input-23-56ccbf5e17f7> in get_middle(n)
3 return n[(len(n)/2) - 1] and n[(len(n)/2)]
4 else:
----> 5 return n[(len(n)/2) + 1]
TypeError: string indices must be integers
I don't understand why I always get the this kind of error. Aren't all my string indices integers?
I know there are are a lot of different solutions out there, but I really would like to know why mine isn't working the way I intended it to.
Thanks in advance!
In Python, there are two kinds of division: integer division and float division.
print(4 / 2)
---> 2.0
print(4 // 2)
---> 2
in Python 2, dividing one integer to an another integer,it comes an integer.
Since Python doesn't declare data types in advance, The interpreter automatically detects the type so you never know when you want to use integers and when you want to use a float.
Since floats lose precision, it's not advised to use them in integral calculations
To solve this problem, future Python modules included a new type of division called integer division given by the operator //
Now, / performs - float division, and
// performs - integer division.
def get_middle(n):
if len(n) % 2 == 0:
return n[(len(n)//2) - 1] and n[(int(len(n)/2))]
else:
return n[int(len(n)/2+ 0.5)]
The issue with our code is that division casts integer to float type automatically and Python starts complaining about it. Simple solution would be to add second / symbol to division or in else case cast it to integer:
def get_middle(n):
if len(n) % 2 == 0:
return n[(len(n)//2) - 1] and n[(len(n)//2)]
else:
return n[int((len(n)/2) + 0.5)]
Try math.floor:
import math
def get_middle(value):
length = len(value)
if length % 2 == 0:
# even length, pick the middle 2 characters
start = length // 2 - 1
end = length // 2 + 1
else:
# odd length, pick the middle character
start = math.floor(length // 2)
end = start + 1
return value[start:end]
A suggestion if you are learning programming, try to break down your steps rather than doing it all in one line, it helps a lot when trying to understand the error messages.
If you divide an odd integer by 2 with the /operator, you get a float. This float should be explicitly converted to an integer when it is used as an indice.

Double Slash Assignment division in Python [duplicate]

This question already has answers here:
What exactly does += do?
(17 answers)
Closed 3 years ago.
I am new to Python and found a function that checks to see if an array of arguments can be made equal by only multiplying the number by 2. However, there is some notation that I do not understand.
Function:
def isEqual(a,n): # a is an arrary, n is the length of array
for i in range(0,n):
while a[i]%2==0:
a[i]//=2 # this is the part I do not understand
print(a[i])
if a[i] != a[0]:
return print("False")
# Otherwise, all elements equal, return true
return print("True")
When I step through the function I see that it replaces the a[i] number by a[i]//2, but I do not understand why you would write // equals to number
I understand the // is "floor" division, but not why someone would write a[i]//=2. I would have thought to write it as a[i]=a[i]//2. I can only assume these are the same things, I just never saw it written this way.
Test code:
a = [50, 4, 2]
n = len(a)
isEqual(a, n)
You might have came across operations that also assign value. Think
a += 1 # same as: a = a + 1
This is exactly the same. It integer divides and assigns the value. Probably better understood with proper spacing:
a //= 2 # same as: a = a // 2

How print("YNEOS"[(w%2)|(w<=2)::2]) works

I was just trying to solve this problem on Codeforces: Check whether it's posible to split a number w (1 ≤ w ≤ 100) into a sum of 2 even numbers
I solved the problem and started to look others solutions for the problem. The I found that solution:
w=int(input())
print("YNEOS"[(w%2)|(w<=2)::2])
it took half of time to solve the problem then mine. Unfortunately I am not getting how it is working and why it is faster than mine. My solution is :
n = int(input())
if (n-2)%2==0 and n > 2:
print("YES")
else:
print("NO")
I am a beginner in python. So it will be very helpful for me it i get a proper explanation.
The :: part is called slicing. The general syntax is start:stop[:step] which will make a new array or string from the original one, beginning from index start to before stop every step (default = 1). It's essentially a syntactic sugar for slice(start, stop, step) which returns a slice object. If stop is omitted then all the elements till the end of the string will be extracted
Understanding slice notation
How do I use the slice notation in Python?
https://docs.python.org/2.3/whatsnew/section-slices.html
You may want to read An Informal Introduction to Python for more information about slicing
Now (w%2)|(w<=2) calculates the start index. | is the bitwise-OR operator. w<=2 is a bool expression, but in Python "Booleans are a subtype of integers", so True and False will be converted to 1 and 0 respectively
If w is an odd number then w % 2 == 1. Regardless of the remaining part, (w%2)|(w<=2) will always be 1 in this case and the expression becomes "YNEOS"[1::2] which takes every second letter in the string starting from index 1. The result is the string NO
If w is an even number then w % 2 == 0. According to the problem description 1 ≤ w ≤ 100 the only valid even numbers belong to the range [2, 100]
If w == 2 then w <= 2 returns 1 and the output is NO like above
If w > 2 then both the sides of (w%2)|(w<=2) is 0, and "YNEOS"[0::2] takes the 1st, 3rd and 5th letters which is YES
Realizing that the expression always returns 0 or 1 we'll have an alternative more readable solution that uses direct addressing instead of slicing
w=int(input())
print(['YES', 'NO'][(w % 2) | (w <= 2)])
Btw, you have a redundant subtraction: (n-2)%2==0 is exactly the same as n % 2 == 0

pop() middle two elements from list

Just doing a review of my Python class and noticed that I forgot how to do this.
def outsideIn2(lst):
'''(list)->list
Returns a new list where the middle two elements have been
removed and placed at the beginning of the result. Assume all lists are an even
length
>>> outsideIn2(['C','a','r','t','o','n'])
['r','t','C','a','o','n'] # rt moves to front
>>> outsideIn2(['H','i'])
['H','i'] # Hi moves to front so output remains the same.
>>> outsideIn2(['B','a','r','b','a','r','a',' ','A','n','n','e'])
['r','a','B','a','r','b,','a',' ','A','n','n','e'] # ra moves to front.
'''
length = len(lst)
middle1 = lst.pop((len(lst) / 2) - 1)
middle2 = lst.pop((len(lst) / 2) + 1)
lst.insert([0], middle1)
lst.insert([1], middle2)
return lst
I'm getting this error:
middle1 = lst.pop((len(lst) / 2) - 1)
TypeError: integer argument expected, got float
What am I doing wrong?
When you upgraded to Python 3, the '/' operator changed from giving you integer division to real division. Switch to '//' operator.
You can use // operator:
middle1 = lst.pop((len(lst) // 2) - 1)
The other answers explained why you are getting the error. You need to use // instead of / (also, just for the record, you need to give list.insert integers, not lists).
However, I'd like to suggest a different approach that uses Explain Python's slice notation:
def outsideIn2(lst):
x = len(lst)//2
return lst[x-1:x+1]+lst[:x-1]+lst[x+1:]
This method should be significantly faster than usinglist.pop and list.insert.
As proof, I made the below script to compare the two methods with timeit.timeit:
from timeit import timeit
def outsideIn2(lst):
length = len(lst)
middle1 = lst.pop((len(lst) // 2) - 1)
middle2 = lst.pop((len(lst) // 2) + 1)
lst.insert(0, middle1)
lst.insert(1, middle2)
return lst
print(timeit("outsideIn2(['B','a','r','b','a','r','a',' ','A','n','n','e'])", "from __main__ import outsideIn2"))
def outsideIn2(lst):
x = len(lst)//2
return lst[x-1:x+1]+lst[:x-1]+lst[x+1:]
print(timeit("outsideIn2(['B','a','r','b','a','r','a',' ','A','n','n','e'])", "from __main__ import outsideIn2"))
The results were as follows:
6.255111473664949
4.465956427423038
As you can see, my proposed method was ~2 seconds faster. However, you can run more tests if you would like to validate mine.
Using pop and insert (especially inserting at positions 0 and 1) can be fairly slow with Python lists. Since the underlying storage for the list is an array, inserting at position 0 means that the element at position n-1 has to be moved to position n, then the element at n-2 has to be moved to n-1 and so on. pop has to do the same in reverse. So imagine in your little method how many element moves must be done. Roughly:
pop #1 - move n/2 elements
pop #2 - move n/2 elements
insert 0 - move n elements
insert 1 - move n elements
So approximately 3n moves are done in this code.
Breaking the list into 3 slices and reassembling a new list may be more optimal:
def outsideIn2(lst):
midstart = len(lst)//2-1
left,mid,right = lst[0:midstart], lst[midstart:midstart+2], lst[midstart+2:]
return mid+left+right
Plus you won't run into any weird issues by pop changing the length of the list between the first and second call to pop. And the slices implicitly guard against index errors when you get a list that is shorter than 2 characters.

Categories