Project Euler 4 Python with Dictionary that adds Digits to Answers - python

I manage to solve the 4th problem of Project Euler & want to take it a step further by not only finding the answer but assigning the corresponding multiplying 3-digits to the answer too. I basically want to assign the corresponding multiples to the palindrome i.e. {palindrome: digit1 digit2}. that is digit1*digit2=palindrome.
For those of you who have not done the Project Euler, here is the question:
A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 × 99. Find the largest palindrome made from the product of two 3-digit numbers.
ns = str(n)
ns_list = []
ns_list_to_int = []
#outside for loop with first 3-digit 'num' and inside loop with second 3-digit 'i'
for num in range (100,1000):
for i in range(100,1000):
n = i*num
#need to convert to string & check if reverse of string == string, if so append to list
ns = str(n)
if ns[::-1] == ns:
ns_list.append({ns:str(i) + ' 'str(num) })
#recreate new list with integers to sort
for i in ns_list:
ns_list_to_int.append(int(i))
#sort in descending order and call the first number
ns_list_to_int = sorted(stuffs, key=int, reverse=True)
ns_list_to_int[0]
tried to get it with ns_list.append({ns:str(i) + ' 'str(num) }) but it doesn't seem to work. Also, not sure how then I would be able to sort it if it is a dictionary.

Instead of appending just the product (your variable ns), you could append a tuple/list/object which contains not only the palindromic number but also the two multiples.
Then all you need to change further down is the sorting function.
Quick and dirty example:
ns_list = []
ns_list_to_int = []
#outside for loop with first 3-digit 'num' and inside loop with second 3-digit 'i'
for num in range (100,1000):
for i in range(100,1000):
n = i*num
#need to convert to string & check if reverse of string == string, if so append to list
ns = str(n)
if ns[::-1] == ns:
pint = int(ns) # convert palindrome to int
t = (i,num,pint) # all the info you need for later
ns_list.append(t)
#sort in descending order and call the first number
ns_list_to_int = sorted(ns_list, key=lambda x:x[2], reverse=True) # sorting by the palindrome
ns_list_to_int[0]

Simple approach is to replace ns_list with a dict, then replace ns_list.append(ns) with ns_dict[num, i] = ns.
Alternatively, to keep the palindromes as the top level iterable component, you can store all the pairs that produced a particular palindrome in a list, using collections.defaultdict(list) to simplify the code:
from collections import defaultdict
ns_dict = defaultdict(list)
#outside for loop with first 3-digit 'num' and inside loop with second 3-digit 'i'
for num in range(100,1000):
for i in range(100,1000):
n = i*num
ns = str(n)
if ns[::-1] == ns:
ns_dict[ns].append((num, i))
Now when you iterate ns_dict, you can see all the pairs you checked that produced that palindrome:
ns_list_with_factors = sorted(ns_dict.items(), key=lambda x: int(x[0]), reverse=True)
print(ns_list_with_factors[0]) # Displays both number and the pairs that produced it
for ns, pairs in ns_dict.items():
...

Related

Kaprekar's constant (sorted(numbers) [duplicate]

This question already has answers here:
How to sort digits in a number?
(5 answers)
Closed 10 months ago.
Okay I've broken down step by step what the function needs to do.
create random number, sort ascending and descending(needs both), subtract, sort the new number ascending and descending and repeat this until the number you get from subtraction is 6174 (preferably I'd like it to loop a time or two after as it should stay at 6174 "which is why it's called Kaprekar's constant".
What I currently have is a random number between 1000,9999. I kept getting (TypeError: 'int' object is not iterable) so I created a list and appended the random number to the list. I'm having issue with sorting the number ascending/descending.
import random
numbers = []
n = random.randint(1000,9999)
print(n)
numbers.append(n)
sorted(numbers)
print(numbers)
So I create a blank list, random number is generated and then printed, the number is then .append to the list and should sort and print the list.
The current output I get is
6988
[6988]
The expected output of what is written is
6988
[6889]
I attempted to use
print(numbers.sort(reverse=True))
this gave "None"
I was expecting it to give [9886]
The only reason this is happening is that it wants to sort multiple items in the list opposed to sorting the numbers in the single item. I'm just not sure how to resolve it.
I'm not quite following you but I think you would like to have the individual digits of the randomly generated number as a list.
If so try doing:
my_str = str(n)
for my_char in my_str:
numbers.append(int(my_char))
instead of:
numbers.append(n)
The first problem is a list.sort method returns None.
import random
numbers = []
n = random.randint(1000,9999)
numbers.append(n)
numbers.sort(reverse=True)
print(numbers)
Also reverse=True does not reverse the element but it reverses the list.
You can check this by this
import random
numbers = []
n = random.randint(1000,9999)
numbers.append(n)
numbers.append(10)
print(sorted(numbers))
print(sorted(numbers,reverse=True))
If you want to reverse element then use this one
import random
lst = []
num = random.randint(1000,9999)
print(num)
lst.append(num)
func = lambda e:int(str(e)[::-1]) # lambda function
lst = list(map(func,sorted(lst)))
print(lst)
NOTE:
1000 after reversing become 1 'cause int('0001') is 1.
4590 after reversing become 954 'cause int('0954') is 954.

IQ test function in Python not working as intended

I'm having trouble with this code below.
My task is to create a function, that among the given numbers finds one that is different in evenness, and returns a position of that number. The numbers are given as a string. So far I have managed to convert the string into an integer list, then using for loop to iterate through each number.
The problem I'm encountering is that I've managed to return only the position of an odd number among the even numbers, and I can't continue on with the code for vice versa action, because it only returns the position of the odd number.
Here is the code:
def iq_test(numbers):
# Splitting the "numbers" string
num_split = numbers.split()
# converting the splitted strings into int
num_map = map(int, num_split)
# converting the object into list
list_num = list(num_map)
for n in list_num:
if not n%2 == 0:
return list_num.index(n) + 1
Your problem is, that you are assuming, that you are searching for the first even number. What you have to do, is to first decide, what you are searching for. You could for example simply first count the number of even numbers. If it is one, then you are looking for an even number, otherwise, you are looking for an odd. As you don't care for the actual numbers, I would map all of them to their value mod 2 as so:
num_map = list(map(lambda x: int(x) % 2, num_split))
Then, the rest is simple. For example like this:
def iq_test(numbers):
# Splitting the "numbers" string
num_split = numbers.split()
# converting the splitted strings into even (0) or odd (1)
num_map = list(map(lambda x: int(x) % 2, num_split))
# return the correct position based on if even or odd is in search
evens = num_map.count(0)
if evens == 1:
return num_map.index(0) + 1
else:
return num_map.index(1) + 1
I came up with a similar and a little bit shorter solution
def iq_test(numbers):
# first check what im looking for "even" or "odd", map a lambda function that basically does it for me, using the numbers argument as a list type and afterwards cast it into a list so i can iterate later on
num_map = list(map(lambda x: 'e' if int(x) % 2 == 0 else 'o', numbers.split()))
# search for even numbers numbers
if num_map.count('e') == 1:
return num_map.index('e') + 1
# search for odd numbers numbers
return num_map.index('o') + 1
def iq_test(numbers):
# Splitting the "numbers" string
num_split = numbers.split()
# converting the splitted strings into int
num_map = map(int, num_split)
# converting the object into list
list_num = list(num_map)
for n in list_num:
if not n%2 == 0:
return list_num.index(n) + 1

Highest number in list from input-numbers in Python

def my_max():
#using input to collect number to list
list_a = input("print your list with numbers: ").split(",")
# Searching for the highest number
max = 0
for i in list_a:
if i > str(max):
max = i
print(max)
my_max()
When i write numbers to input, sometimes the highest number is being printed, but not always.
For an example, if i write :"54,64,446 "
the number "64 is being printed. Do anybody knows why?
You need to map it into list of ints before you do the logic:
def my_max():
# using input to collect number to list
list_a = input("print your list with numbers: ").split(",")
# Searching for the highest number
return max(map(int, list_a))
print(my_max())
Sample run:
print your list with numbers: 54,64,446
446
Splitting on ',' gives you a list of strings. What you observed is an expected behaviour because you find max of a list of strings in contrast to list of integers.
Without using a max(), I would go something like this:
def my_max():
# using input to collect number to list
list_a = list(map(int, input("print your list with numbers: ").split(",")))
# Searching for the highest number
max = list_a[0]
for x in list_a[1:]:
if x > max:
max = x
return max
print(my_max())
Your list_a contains strings, not numbers. When you do your comparison, you are comparing the values of these strings. The result of this is the highest value alphabetically, rather than numerically.
Taken as strings rather than numbers, 64 > 54 > 446

Intro to Python - Lists questions

we've started doing Lists in our class and I'm a bit confused thus coming here since previous questions/answers have helped me in the past.
The first question was to sum up all negative numbers in a list, I think I got it right but just want to double check.
import random
def sumNegative(lst):
sum = 0
for e in lst:
if e < 0:
sum = sum + e
return sum
lst = []
for i in range(100):
lst.append(random.randrange(-1000, 1000))
print(sumNegative(lst))
For the 2nd question, I'm a bit stuck on how to write it. The question was:
Count how many words occur in a list up to and including the first occurrence of the word “sap”. I'm assuming it's a random list but wasn't given much info so just going off that.
I know the ending would be similar but no idea how the initial part would be since it's string opposed to numbers.
I wrote a code for a in-class problem which was to count how many odd numbers are on a list(It was random list here, so assuming it's random for that question as well) and got:
import random
def countOdd(lst):
odd = 0
for e in lst:
if e % 2 = 0:
odd = odd + 1
return odd
lst = []
for i in range(100):
lst.append(random.randint(0, 1000))
print(countOdd(lst))
How exactly would I change this to fit the criteria for the 2nd question? I'm just confused on that part. Thanks.
The code to sum -ve numbers looks fine! I might suggest testing it on a list that you can manually check, such as:
print(sumNegative([1, -1, -2]))
The same logic would apply to your random list.
A note about your countOdd function, it appears that you are missing an = (== checks for equality, = is for assignment) and the code seems to count even numbers, not odd. The code should be:
def countOdd(lst):
odd = 0
for e in lst:
if e%2 == 1: # Odd%2 == 1
odd = odd + 1
return odd
As for your second question, you can use a very similar function:
def countWordsBeforeSap(inputList):
numWords = 0
for word in inputList:
if word.lower() != "sap":
numWords = numWords + 1
else:
return numWords
inputList = ["trees", "produce", "sap"]
print(countWordsBeforeSap(inputList))
To explain the above, the countWordsBeforeSap function:
Starts iterating through the words.
If the word is anything other than "sap" it increments the counter and continues
If the word IS "sap" then it returns early from the function
The function could be more general by passing in the word that you wanted to check for:
def countWordsBefore(inputList, wordToCheckFor):
numWords = 0
for word in inputList:
if word.lower() != wordToCheckFor:
numWords = numWords + 1
else:
return numWords
inputList = ["trees", "produce", "sap"]
print(countWordsBeforeSap(inputList, "sap"))
If the words that you are checking come from a single string then you would initially need to split the string into individual words like so:
inputString = "Trees produce sap"
inputList = inputString.split(" ")
Which splits the initial string into words that are separated by spaces.
Hope this helps!
Tom
def count_words(lst, end="sap"):
"""Note that I added an extra input parameter.
This input parameter has a default value of "sap" which is the actual question.
However you can change this input parameter to any other word if you want to by
just doing "count_words(lst, "another_word".
"""
words = []
# First we need to loop through each item in the list.
for item in lst:
# We append the item to our "words" list first thing in this loop,
# as this will make sure we will count up to and INCLUDING.
words.append(item)
# Now check if we have reached the 'end' word.
if item == end:
# Break out of the loop prematurely, as we have reached the end.
break
# Our 'words' list now has all the words up to and including the 'end' variable.
# 'len' will return how many items there are in the list.
return len(words)
lst = ["something", "another", "woo", "sap", "this_wont_be_counted"]
print(count_words(lst))
Hope this helps you understand lists better!
You can make effective use of list/generator comprehensions. Below are fast and memory efficient.
1. Sum of negatives:
print(sum( i<0 for i in lst))
2. Count of words before sap: Like you sample list, it assumes no numbers are there in list.
print(lst.index('sap'))
If it's a random list. Filter strings. Find Index for sap
l = ['a','b',1,2,'sap',3,'d']
l = filter(lambda x: type(x)==str, l)
print(l.index('sap'))
3. Count of odd numbers:
print(sum(i%2 != 0 for i in lst))

Displaying possible elements from list of list within the range of input floats

Let's say i have a list of list containing:
L = [['10.2','9.1','G'],['12.9','7.4','H'],['5.6','4.3','G'],['5.7','4.5','G']]
where the alphabets in each list within the list of list represents something like 'type'
In this case, python will request for the user input of four float numbers separated by ':', for example;
input = 5.5:4.4:5.7:4.7
Before python proceed on dealing with the input, as shown in the list of list, the alphabets in each list at the third section represents a type therefore;
For example, upon user input, python will compare the number of the input to the values in the list of list within the range of the user input of type 'G'.
Hence, python will output the list from the list of list in which the numbers are in range as the user input. So,
input = 5.5:4.4:5.7:4.6
output = [5.6,4.3] and [5.7,4.5]
note: the input consist of four float numbers separated by ':' and we can assume the first half is a set 5.5:4.4 and the second half is a set 5.7:4.6.
I gave it a try but i don't know how i would be able to output the list within range to the input.
L = [['10.2','9.1','G'],['12.9','7.4','H'],['5.6','4.3','G'],['5.8','4.5','G']]
userinput = input("Enter floats:") #example 5.5:4.4:5.7:4.6
strSeparate = userinput.split(':')
floatInput = [float(i) for i in strSeparate] #turn input into float
inputList = [floatInput[:2],floatInput[2:]] #[[5.5,4.4],[5.7,4.6]]
for line in L:
for val in inputList:#???
output format would be:
[[5.6,4.3],[5.7,4.5]]
You can do it as shown below.
First the user input is split on the :, the values are converted to floats, and an iterator is created to help pair the values with zip(). Then each pair is compared with the ranges in L. A pair lies within the range if both of its values lie between the upper and lower values of the range. Any pair that lies within the range is added to the results list.
L = [['10.2','9.1','G'],['12.9','7.4','H'],['5.6','4.3','G'],['5.8','4.5','G']]
inputs = [float(s) for s in '5.5:4.4:5.7:4.6'.split(':')]
it = iter(inputs)
results = []
for pair in zip(it, it):
for line in L:
if line[2] == 'G':
upper = float(line[0])
lower = float(line[1])
if ((lower <= pair[0] <= upper) and
(lower <= pair[1] <= upper)):
results.append([upper, lower])
print(results)
This will output:
[[5.6, 4.3], [5.8, 4.5]]
Note that this code will include duplicate values in results if there is more than one input pair that fall within a range. If this is not wanted you can use a set instead of a list for results, and add tuples to the set (because lists are not hashable).
Also this assumes that the upper and lower bounds for each sub list in L is in order (upper then lower). If that's not the case you can do this:
upper = float(line[0])
lower = float(line[1])
lower, upper = lower, upper if lower <= upper else upper, lower
The solution using numpy.arange() and numpy.any() funcions:
import numpy as np
L = [['10.2','9.1','G'],['12.9','7.4','H'],['5.6','4.3','G'],['5.7','4.5','G']]
userinput = "5.5:4.4:5.7:4.6" #example 5.5:4.4:5.7:4.6
floatInput = [float(i) for i in userinput.split(':')] #turn input into float
result = []
for i in (floatInput[0:2], floatInput[2:]):
r = np.arange(i[1], i[0], 0.1) # generating float numbers range
items = [l[0:2] for l in L
if isinstance(np.any([r[r >= float(l[0])], r[r >= float(l[1])]]), np.ndarray)
and l[0:2] not in result]
if (items): result.extend(items)
print(result)
The output:
[['5.6', '4.3'], ['5.7', '4.5']]

Categories