python: list index out of range error in code - python

I'm pretty new to python, and really stuck at this.
Basically, I'm supposed to make a check code to check the last alphabet of the NRIC. My code works fine so long as there are 7 numbers (like there are supposed to be). However, my teacher just helped me find out that my code doesn't work whenever the number starts with 0. Below is my code.
def check_code():
nricno = int(input("Please enter your NRIC(numbers only). If you don't type an nric number, this code will fail."))
NRIC = [ int(x) for x in str(nricno) ]
a = NRIC[0]*2
b = NRIC[1]*7
c = NRIC[2]*6
d = NRIC[3]*5
e = NRIC[4]*4
f = NRIC[5]*3
g = NRIC[6]*2
SUM = int(a + b + c + d + e + f +g)
remainder = int(SUM % 11)
leftovers = int(11 - remainder)
rightovers = leftovers - 1
Alphabet = "ABCDEFGHIZJ"
checkcode = chr(ord('a') + rightovers)
print(checkcode)
check_code()
This is the way the NRIC is supposed to be calculated, in the image below.
NRIC calculation help.

When you convert the string input into an int, the leading zero is stripped away (e.g. "0153444" -> 153444). When you convert back to a string again in the list comprehension, you won't get the zero back, so you end up with an NRIC list of [1, 5, 3, 4, 4, 4] instead of [0, 1, 5, 3, 4, 4, 4]. If you remove the int call, like this, you won't lose the leading zero.
# Change this:
nricno = int(input("Please enter your NRIC(numbers only)..."))
# To this:
nricno = input("Please enter your NRIC(numbers only)...")

Here's a compact way to calculate the NRIC check code. If an invalid string is passed to the function a ValueError exception is raised, which will cause the program to crash. And if a non-string is passed TypeError will be raised. You can catch exceptions using the try:... except syntax.
def check_code(nric):
if len(nric) != 7 or not nric.isdigit():
raise ValueError("Bad NRIC: {!r}".format(nric))
weights = (2, 7, 6, 5, 4, 3, 2)
n = sum(int(c) * w for c, w in zip(nric, weights))
return "ABCDEFGHIZJ"[10 - n % 11]
# Test
nric = "9300007"
print(check_code(nric))
output
B

Edit: This code verifies if the input is made of 7 digits.
def check_code():
while True:
nricno = input("Please enter your NRIC(numbers only). If you don't type an nric number, this code will restart.")
if len(nricno) == 7 and nricno.digits == True:
print ("Works")
continue
else:
print("Error, 7 digit number was not inputted and/or letters and other characters were inputted.")
a = NRIC[0]*2
b = NRIC[1]*7
c = NRIC[2]*6
d = NRIC[3]*5
e = NRIC[4]*4
f = NRIC[5]*3
g = NRIC[6]*2
SUM = int(a + b + c + d + e + f +g)
remainder = int(SUM % 11)
print(remainder)
leftovers = int(11 - remainder)
rightovers = leftovers - 1
Alphabet = "ABCDEFGHIZJ"
checkcode = chr(ord('a') + rightovers)
print(checkcode.upper())
check_code()

When you force your input as an int, the leading 0 will be interpreted as an error in Python 3. For example, int(0351) would not yield either 0351 or 351 but would just cause an error stating invalid token.
You should not force the input to be an int, but instead add an assert statement stating that the value inputted must be a 7 digit integer (or a regular statement as you've done if you prefer).
Change this:
nricno = int(input("Please enter your NRIC(numbers only). If you don't type an nric number, this code will fail."))
To this:
nricno = input("Please enter your NRIC(numbers only). If you don't type an nric number, this code will fail.")

Related

Problem with the Return Function in Python in my code

I have been trying to write code for the activity selection problem. I am not using the greedy algorithm method but I just wrote this code on my own. My cod is able to find the max number of activities possible, but in my code, I am having a problem with the input. Here's my code:
i = int(input("How many activities are there? Please enter as an integer. "))
list_of_lists = [[[]], [], []]
def check(space):
x = space.split(" ")
for b in range(2):
x[b] = int(x[b])
if (x[0] >= x[1]):
space = input("Incorrect start and end times. Please enter a start time which is less than the end time. ")
check(space)
return space
return space
#ab = space
#return ab
for a in range(1, i + 1):
arr = input("Please enter the start time and end time. There should be a space between the two integers. ")
abc = check(arr)
print(abc)
list_of_lists[a].append(list(map(int, abc.split(' '))))
list_of_lists[0].append(list(map(int, abc.split(' '))))
print(list_of_lists)
count = [0] * i
inn = 0
for ii in range(1, i + 1):
for jj in range(1, i + 1):
if (ii != jj):
if (list_of_lists[ii][0][0] <= list_of_lists[0][jj][0]):
a = list_of_lists[ii][0][1]
b = list_of_lists[0][jj][0]
else:
a = list_of_lists[0][jj][1]
b = list_of_lists[ii][0][0]
if (a <= b):
count[inn] += 1
else:
count[inn] += 1
inn += 1
count.sort()
print(count[i - 1])
So there is a problem with the check function. It is working perfectly when I have the correct type of input. But say, if someone inputs the wrong type of input, then there is a problem. Let me describe with an example.
If I input 2 4 to arr = ..., then it is working properly. If I input 6 5 to arr = ... and then if I input 2 4 to space = input(..., it is working properly. However, if say I input 6 5 to arr = ..., then I input 5 4 to space = input(..., and then I input 2 4 to space = input(..., then it is not working properly. check function is returning 5 4. I know the problem is with the return but I really do not know how to solve this issue so that it works in the right way.

sum of every other digit and doubling the rest

I'm working on a card number check code, for now I created a function that asks for the card number and checks if it is 8 digits or not (it has to be 8) and then calls another function that will do the math and check if the card is valid. For this function:
Starting from the rightmost digit, form the sum of every other digit. For example, if the card number is 1234 5678, then you form the sum 8 + 6 + 4 + 2 = 20
Double each of the digits that were not included in the preview step and then add all digits of the resulting numbers. For example, the digits that were not included are 7 5 3 1, we double the, 14 10 6 2, and then we sum each digit, 1 + 4 + 1 + 0 + 6 + 2 = 14
Add the sums of the two steps, 20 + 14 = 34, if the last digit is 0 then the card is valid, otherwise it is not valid (which is our case)
My problem is that I don't know how to iterate and get the sum of every other digit or double the other number which were not included in step 2. My thought was to use a while loop but then what?
EDIT: since some answers used lists... we didn't study lists yet, so I should not use it, we are only allowed to use sample stuff, loops, functions, etc.. even sum(map()) we didn't study
That is my code for now (its not much but just thought put it anyway)
def getCard():
CardInput = int(input("Enter your 8 digit credit card number: "))
if len(CardInput) == 8:
CardCheck(CardInput)
else:
print("Invalid Input: Should be exactly 8 digits!")
getCard()
def CardCheck(CardNumber):
Position = 0
Sum = 0
DoubleSum = 0
FinalSum = 0
while CardNumber >= 0:
Position += 1
So, the ugly way of doing is, you can write a for loop and use indexing for access specific elements
for i in range(len(CardInput)):
# it will iterate from 0 to 7
print(CardInput[i]) # here you get ith element
if i % 2 == 1:
print("I am other!") # you can sum your things here into another variable
Or with while:
while position < len(CardInput):
print(CardInput[position])
position += 1
It assumes CardInput is str, so I recommend to not convert it earlier.
However pythonic way would be
sum(map(int, CardInput[1::2])))
CardInput[1::2] returns list of every second element starting from second (0 is first).
map converts every element to in.
sum sums elements.
prompt = "Enter the eight-digit number: "
while True:
number = input(prompt)
if len(number) == 8 and number.isdigit():
break
prompt = "Oops, try again: "
first_digits = number[1::2] # If the user entered '12345678', this will be the substring '2468'
first_sum = sum(map(int, first_digits)) # Take each digit (character), map it to a single-digit integer, and take the sum of all single-digit integers
second_digits = number[0::2] # If the user entered '12345678', this will be the substring '1357'
doubled_ints = [int(char) * 2 for char in second_digits] # Take each digit (character), turn it into an integer, double it, and put it in a list.
second_sum = sum(map(int, "".join(map(str, doubled_ints)))) # Merge all integers in 'doubled_ints' into a single string, take each character, map it to a single digit integer, and take the sum of all integers.
total_sum = first_sum + second_sum
total_sum_last_digit = str(total_sum)[-1]
is_valid_card = (total_sum_last_digit == '0')
if is_valid_card:
print("Your card is valid (total sum: {})".format(total_sum))
else:
print("Your card is NOT valid (total sum: {})".format(total_sum))
def getCard():
CardInput = input("Enter your 8 digit credit card number: ")
if len(CardInput) == 8:
CardCheck(CardInput)
else:
print("Invalid Input: Should be exactly 8 digits!")
getCard()
def CardCheck(CardNumber):
list_CardNumber = [x for x in "25424334"]
Sum = sum(int(x) for x in list_CardNumber[1:8:-2])
DoubleSum = 2*sum(int(x) for x in list_CardNumber[0:8:-2])
FinalSum = Sum + DoubleSum
if str(FinalSum)[-1] == "0":
print("Valid Input")
else:
print("Invalid Input")
To get you started, you should check out enumerate(), it'll simplify things if you're just going to use loops by giving you easy access to both the index and value every loop.
step1 = 0
for i, x in enumerate(number):
if i % 2:
print('index: '+ str(i), 'value: '+ x)
step1 += int(x)
print('step 1: ', step1)
Output:
index: 1 value: 2
index: 3 value: 4
index: 5 value: 6
index: 7 value: 8
step 1: 20
You can use:
# lets say
CardNumber = '12345678'
# as mentioned by kosciej16
# get every other digit starting from the second one
# convert them to integers and sum
part1 = sum(map(int, CardNumber[1::2]))
# get every other digit starting from the first one
# convert them to integers and double them
# join all the digits into a string then sum all the digits
part2 = sum(map(int,''.join(list(map(lambda x: str(int(x)*2), CardNumber[0::2])))))
result = part1 + part2
print(result)
Output:
34
Edit:
Only with loops you can use:
# lets say
CardNumber = '12345678'
total_sum = 0
for idx, digit in enumerate(CardNumber):
if idx % 2 == 1:
total_sum += int(digit)
else:
number = str(int(digit)*2)
for d in number:
total_sum += int(d)
print(total_sum)
output:
34
Since you need to iterate over the digits, it's actually easier IMO if you leave it as a string, rather than converting the input to an int; that way you can just iterate over the digits and convert them to int individuall to do math on them.
Given an 8-digit long string card, it might look like this, broken into steps:
even_sum = sum(int(n) for n in card[1::2])
double_odds = (2 * int(n) for n in card[0::2])
double_odd_sum = sum(int(c) for do in double_odds for c in str(do))
All together with some logic to loop while the input is invalid:
def get_card() -> str:
"""Returns a valid card number, or raises ValueError."""
card = input("Enter your 8 digit credit card number: ")
if len(card) != 8 or not card.isdecimal():
raise ValueError("Invalid input: Should be exactly 8 digits!")
card_check(card)
return card
def card_check(card: str) -> None:
"""Raises ValueError if card checksum fails, otherwise returns None."""
even_sum = sum(int(n) for n in card[1::2])
double_odds = (2 * int(n) for n in card[::2])
double_odd_sum = sum(int(c) for do in double_odds for c in str(do))
if (even_sum + double_odd_sum) % 10:
raise ValueError("Card checksum failed!")
while True:
try:
print(f"{get_card()} is a valid card number!")
except ValueError as e:
print(e)

TypeError: unsupported operand type(s) for ** or pow(): 'int' and 'set'

Question:
Without using any string methods, try to print the following:
123...n
Note that "..." represents the consecutive values in between.
Example
n=5
Prints 12345.
my solution
n = int(input())
sum=0
i=n
while i>0:
sum=sum + i*(10**{n-i})
i -= 1
print(sum)
First: {n-i} will evaluate to {-1} if n=0 since {x} is way to express a set in python
Second: you're asking for method to print numeric string, but no string operation (so all concatenation should be done in addition between two integers). Here I'm assuming that the accepted input can only be positive number
e.g.:
input 5, output=12345
input 12, output=123456789101112
When learning to solve such 'challenge' problem, it's better to do it test driven: write a simple program that just work, then compare/assert with generated expected result
this is the correct but not acceptable way to generate the output (with string operation):
inp = int(input())
expected = ""
for i in range(1, inp+1):
expected = expected + str(i)
print(expected)
Then try to solve it gradually: assume single digit input only. Here we got the idea that in order to place a number beside other number, we need to multiply first number by 10, then next number by 1. So your solution for making it multiplied by power of ten is already on correct track
now we can write:
inp = int(input())
result = 0
for i in range(1, inp+1):
power_of_ten = 10**(inp-i)
print("pot", power_of_ten)
result = result + (i*power_of_ten)
print("r", result)
print(result)
output:
5
pot 10000
r 10000
pot 1000
r 12000
pot 100
r 12300
pot 10
r 12340
pot 1
r 12345
12345
at this point, we can try to assert if our output is the same with our generated output (the one that use string operation):
inp = int(input())
result = 0
for i in range(1, inp+1):
power_of_ten = 10**(inp-i)
result = result + (i*power_of_ten)
print(result)
expected = ""
for i in range(1, inp+1):
expected = expected + str(i)
print(expected)
assert(result == int(expected))
print("assertion passed")
output:
5
12345
12345
assertion passed
but if we use two digit input, the output will no longer be correct:
12
123456790122
123456789101112
Traceback (most recent call last):
File "/tmp/c.py", line 14, in <module>
assert(result == int(expected))
AssertionError
so, if we have to output 123456789101112 when we input 12, then we need a mathematical function (not a string function) that can count the number of digit in a number:
output 2 if we input 12, 40, 99, 80 (two digit number)
output 1 if we input 1, 5, 2 (one digit number)
etc.
such function is called logarithm function: e.g.:
math.floor(math.log(i, 10)) + 1
first we try to logarithm the input to base 10, then we floor the result (so that the result is not a decimal/fractional number); then we add 1
here is the code that incorporate that: note that for simplicity, we're looping backward (e.g.: 12,11,10,9..1)
import math
inp = int(input())
result = 0
pad = 0
for i in range(inp, 0, -1):
result = result + i*10**pad
pad = pad + math.floor(math.log(i, 10)) + 1
print(result)
expected = ""
for i in range(1, inp+1):
expected = expected + str(i)
print(expected)
assert(result == int(expected))
print("assertion passed")
here I added a variable pad that will contain the number of pad to be added on next iteration, e.g.: input=5
iteration=1 i=5 pad=1 result=5 (so next number, i.e: 4, will be multiplied with 10^1)
iteration=2 i=4 pad=2 result=45 (so next number, i.e: 3, will be multiplied with 10^2)
iteration=3 i=3 pad=3 result=345
iteration=4 i=2 pad=4 result=2345
iteration=5 i=1 pad=5 result=12345
when input=12
iteration=1 i=12 pad=2 result=12
iteration=2 i=11 pad=4 result=1112
iteration=3 i=10 pad=6 result=101112
iteration=4 i=9 pad=7 result=9101112
iteration=5 i=8 pad=8 result=89101112
iteration=6 i=7 pad=9 result=789101112
iteration=7 i=6 pad=10 result=6789101112
iteration=8 i=5 pad=11 result=56789101112
iteration=9 i=4 pad=12 result=456789101112
iteration=10 i=3 pad=13 result=3456789101112
iteration=11 i=2 pad=14 result=23456789101112
iteration=12 i=1 pad=15 result=123456789101112
output:
$ python3 /tmp/a.py
5
12345
12345
assertion passed
$ python3 /tmp/a.py
12
123456789101112
123456789101112
assertion passed
so the final code is:
import math
inp = int(input())
result = 0
pad = 0
for i in range(inp, 0, -1):
result = result + i*10**pad
pad = pad + math.floor(math.log(i, 10)) + 1
print(result)

Formatting unknown output in a table in Python

Help! I'm a Python beginner given the assignment of displaying the Collatz Sequence from a user-inputted integer, and displaying the contents in columns and rows. As you may know, the results could be 10 numbers, 30, or 100. I'm supposed to use '\t'. I've tried many variations, but at best, only get a single column. e.g.
def sequence(number):
if number % 2 == 0:
return number // 2
else:
result = number * 3 + 1
return result
n = int(input('Enter any positive integer to see Collatz Sequence:\n'))
while sequence != 1:
n = sequence(int(n))
print('%s\t' % n)
if n == 1:
print('\nThank you! The number 1 is the end of the Collatz Sequence')
break
Which yields a single vertical column, rather than the results being displayed horizontally. Ideally, I'd like to display 10 results left to right, and then go to another line. Thanks for any ideas!
Something like this maybe:
def get_collatz(n):
return [n // 2, n * 3 + 1][n % 2]
while True:
user_input = input("Enter a positive integer: ")
try:
n = int(user_input)
assert n > 1
except (ValueError, AssertionError):
continue
else:
break
sequence = [n]
while True:
last_item = sequence[-1]
if last_item == 1:
break
sequence.append(get_collatz(last_item))
print(*sequence, sep="\t")
Output:
Enter a positive integer: 12
12 6 3 10 5 16 8 4 2 1
>>>
EDIT Trying to keep it similar to your code:
I would change your sequence function to something like this:
def get_collatz(n):
if n % 2 == 0:
return n // 2
return n * 3 + 1
I called it get_collatz because I think that is more descriptive than sequence, it's still not a great name though - if you wanted to be super explicit maybe get_collatz_at_n or something.
Notice, I took the else branch out entirely, since it's not required. If n % 2 == 0, then we return from the function, so either you return in the body of the if or you return one line below - no else necessary.
For the rest, maybe:
last_number = int(input("Enter a positive integer: "))
while last_number != 1:
print(last_number, end="\t")
last_number = get_collatz(last_number)
In Python, print has an optional keyword parameter named end, which by default is \n. It signifies which character should be printed at the very end of a print-statement. By simply changing it to \t, you can print all elements of the sequence on one line, separated by tabs (since each number in the sequence invokes a separate print-statement).
With this approach, however, you'll have to make sure to print the trailing 1 after the while loop has ended, since the loop will terminate as soon as last_number becomes 1, which means the loop won't have a chance to print it.
Another way of printing the sequence (with separating tabs), would be to store the sequence in a list, and then use str.join to create a string out of the list, where each element is separated by some string or character. Of course this requires that all elements in the list are strings to begin with - in this case I'm using map to convert the integers to strings:
result = "\t".join(map(str, [12, 6, 3, 10, 5, 16, 8, 4, 2, 1]))
print(result)
Output:
12 6 3 10 5 16 8 4 2 1
>>>

Programme to print mulitples of 5 in a range specified by user

Im new to python and im trying to write a code which determine all the multiples of 5 in a range specified by the user. Im getting the code to go up in units of 5. eg if the range was 6 and 21, it would say that the multiples are 6,11,16,21.
a = int(input("Enter a value of a : "))
b = int(input("Enter a vlue of b : "))
if a%5 ==0:
a = a+5
multiples = []
for value in range(a, b+1,5):
multiples.append(value)
print(multiples)
I'm expected just multiples of 5 to be printed.
If your expected output from the range 6 to 21 is [10, 15, 20], you can first test if the starting point is a multiple of 5 as you did with if a % 5 == 0, and if it is true, we can add 5 repeatedly until we hit the end of range, while appending all the steps into the output list.
However if a % 5 == 0 gives you false, then we need to find the value to add to this starting point so that we can get the first multiple of 5 in this range, i.e.
diff = 5 - a % 5
and add this value to the starting point a, we get the first value:
first = a + diff
Then we can add 5 repeatedly until we hit the end of range.
To put all the above into code:
# get user input for the range a to b
a = int(input("Enter a value of a : "))
b = int(input("Enter a value of b : "))
output_list = []
# determine first value
if a % 5 == 0:
first_val = a
else:
first_val = a + (5 - (a % 5))
# go through the range between the first value and the end, with step=5
for val in range(first_val, b + 1, 5):
# append value to the list
output_list.append(val)
print(output_list)
You could add the following to your if statement:
else: # if it isn't a multiple of 5
a += (5-a%5) # add enough to get to the next multiple of 5
I think you need to find the reminder of input1 and then start looping with that reminder
a = int(input("Enter a value of a : "))
b = int(input("Enter a vlue of b : "))
c=a%5 #reminder
multiples = []
for value in range(a+5-c, b+1,5):
multiples.append(value)
print(multiples)

Categories

Resources