Compute the last (decimal) digit of x1 ^ (x2 ^ (x3 ^ (... ^ xn))) [duplicate] - python

This question already has answers here:
Last digit of power list
(2 answers)
Closed 4 years ago.
I need to find the unit digit of x1 ^ (x2 ^ (x3 ^ (... ^ xn))) from integers passed into the function as a list.
For example the input [3, 4, 2] would return 1 because 3 ^ (4 ^ 2) = 3 ^ 16 = 43046721 the last digit of which is 1.
The function needs to be efficient as possible because obviously trying to calculate 767456 ^ 981242 is not very quick.
I have tried a few methods but I think the best way to solve this is using sequences. For example any number ending in a 1, when raised to a power, will always end in 1. For 2, the resulting number will end in either 2, 4, 6 or 8.
If a number is raised to a power, the last digit of the resulting number will follow a pattern based on the last digit of the exponent:
1: Sequence is 1
2: Sequence is 2, 4, 8, 6
3: Sequence is 3, 9, 7, 1
4: Sequence is 4, 6
5: Sequence is 5
6: Sequence is 6
7: Sequence is 7, 9, 3, 1
8: Sequence is 8, 4, 2, 6
9: Sequence is 9, 1
0: Sequence is 0
I think the easiest way to calculate the overall last digit is to work backwards through the list and calculate the last digit of each calculation one at a time until I get back to the start but I am not sure how to do this?
If anyone could help or suggest another method that is equally or more efficient than that would be appreciated.
I have this code so far but it does not work for very large numbers
def last_digit(lst):
if lst == []:
return 1
total = lst[len(lst)-2] ** lst[len(lst)-1]
for n in reversed(range(len(lst)-2)):
total = pow(lst[n], total)
return total%10
Edit: 0 ^ 0 should be assumed to be 1

x^n = x^(n%4) because the last digit always has a period of 4.
x ^2 ^3 ^4 ^5
1 1 1 1 1
2 4 8 6 2
3 9 7 1 3
4 6 4 6 4
5 5 5 5 5
6 6 6 6 6
7 9 3 1 7
8 4 2 6 8
9 1 9 1 9
As you can see, all 9 digits have a period of 4 so we can use %4 to make calculations easier.
There's also a pattern if we do this %4.
x ^0 ^1 ^2 ^3 ^4 ^5 ^6 ^7 ^8 ^9
1 1 1 1 1 1 1 1 1 1 1
2 1 2 0 0 0 0 0 0 0 0
3 1 3 1 3 1 3 1 3 1 3
4 1 0 0 0 0 0 0 0 0 0
5 1 1 1 1 1 1 1 1 1 1 (all %4)
6 1 2 0 0 0 0 0 0 0 0
7 1 3 1 3 1 3 1 3 1 3
8 1 0 0 0 0 0 0 0 0 0
9 1 1 1 1 1 1 1 1 1 1
As shown, there is a pattern for each x when n>1. Therefore, you can see that (x^n)%4 = (x^(n+4k))%4 when n>1. We can then prevent the issues that arises from n=0 and n=1 by adding 4 to n. This is because, if (x^n)%4 = (x^(n+4k))%4, then (x^n)%4 = (x^(n%4+4))%4 as well.
powers = [3, 9, 7, 1]
lastDigit = 1
for i in range(len(powers) - 1, -1, -1):
if lastDigit == 0:
lastDigit = 1
elif lastDigit == 1:
lastDigit = powers[i]
else:
lastDigit = powers[i]**(lastDigit%4+4)
print(lastDigit%10)

This is more math than programming. Notice that all the sequences you listed has length either 1, 2, or 4. More precisely, x^4 always ends with either 0, 1, 5, 6, as does x^(4k). So if you know x^(m mod 4) mod 10, you know x^m mod 10.
Now, to compute x2^(x3^(...^xn)) mod 4. The story is very similar, x^2 mod 4 is ether 0 if x=2k or 1 if x=2k+1 (why?). So
is 0 if x2 == 0
is 1 if x2 > 0 and x3 == 0
if x2 is even, then it is either 2 or 0 with 2 occurs only when x2 mod 4 == 2 and (x3==1 or (any x4,...xn == 0) ).
if x2 is odd, then x2^2 mod 4 == 1, so we get 1 if x3 is even else x2 mod 4.
Enough math, let's talk coding. There might be corner cases that I haven't cover, but it's should work for most cases.
def last_digit(lst):
if len(lst) == 0:
return 1
x = lst[0] % 10
if len(lst) == 1:
return x
# these number never change
if x in [0,1,5,6]:
return x
# now we care for x[1] ^ 4:
x1 = x[1] % 4
# only x[0] and x[1]
if len(lst) == 2 or x1==0:
return x[0] ** x1 % 10
# now that x[2] comes to the picture
if x1 % 2: # == 1
x1_pow_x2 = x1 if (x[2]%2) else 1
else:
x1_pow_x2 = 2 if (x1==2 and x[2]%2 == 1) else 0
# we almost done:
ret = x ** x1_pow_x2 % 10
# now, there's a catch here, if x[1]^(x[2]^...^x[n-1]) >= 4,
# we need to multiply ret with the last digit of x ** 4
if x[1] >=4 or (x[1] > 1 and x[2] > 1):
ret = (ret * x**4) % 10
return ret

Working off of your sequences idea and fleshing it out, you'd want to create a dictionary that can map all relevant sequences.
mapping = {}
for i in range(1, 10):
mapping[i] = [i]
last_digit = i
while True:
last_digit *= i
last_digit = last_digit%10
if last_digit in mapping[i]:
break
else:
mapping[i].append(last_digit)
print(mapping)
This produces Output: mapping
{1: [1],
2: [2, 4, 8, 6],
3: [3, 9, 7, 1],
4: [4, 6],
5: [5],
6: [6],
7: [7, 9, 3, 1],
8: [8, 4, 2, 6],
9: [9, 1]}
Now the real logic can start, The key takeaway is that the pattern repeats itself after the sequence is completed. So, it does not matter how big the power is, if you just use a modulo and figure out which position of the sequence it should occupy.
def last_digit_func(lst, mapping):
if lst == []: #taken from OP
return 1
last_digit = lst[0] % 10
if 0 in lst[1:]: #edge case 0 as a power
return 1
if last_digit == 0: #edge case 0 at start
return last_digit
for current_power in lst[1:]:
if len(mapping[last_digit]) == 1:
return last_digit
ind = current_power % len(mapping[last_digit])
ind -= 1 #zero indexing, but powers start from 1.
last_digit = mapping[last_digit][ind]
return last_digit
test1 = [3, 4, 2]
print(last_digit_func(test1, mapping)) #prints 1
I verified this by calculating the powers in python.
test2 = [767456 , 981242]
print(last_digit_func(test2, mapping)) #prints 6
And i tried to verify this by running it in python....i now have instant regrets and my program is still trying to solve it out. Oh well :)

Related

how to navigate a nested loop

I am new to python and I've been trying to wrap my head around this code:
stop = int(input())
result = 0
for a in range(4):
print(a, end=': ')
for b in range(2):
result += a + b
if result > stop:
print('-', end=' ')
continue
print(result, end=' ')
print()
When I input 6, the output is
0: 0 1
1: 2 4
2: 6 -
3: - -
why isn't it
0: 0 1
1: 3 4 --> since we're starting out with a = 1 and b = 2 so result is 1 + 2 = 3.
etc
I feel like I'm missing something fundamental.
Value of b will never be 2.
Each iteration of loop will initialise the scope variables. i.e. while looping first loop, value of b will range between 0 & 1.
Whereas, Value of result (a global variable) will be cumulative (value obtained from prev iteration).
iteration
a
b
result
output
1
0
0
0
0: 0..
2
0
1
1
0: 0 1
3
1
0
2
1: 2..
4
1
1
4
1: 2 4
5
2
0
6
2: 6..
6
2
1
9
2: 6 9
7
3
0
12
3: 12..
8
3
1
16
3: 12 16
when a = 0 and b = 1, result = 0 + 0 + 1 = 1,
so, for a = 1 and b = 0, result = 1 + 1 + 0 = 2
when a = 1 and b = 1, result = 2 + 1 + 1 = 4
so, it will print 1: 2 4

Calculating probability of consecutive events with python pandas

Given a dataframe, how do I calculate the probability of consecutive events using python pandas?
For example,
Time
A
B
C
1
1
1
1
2
-1
-1
-1
3
1
1
1
4
-1
-1
-1
5
1
1
1
6
-1
-1
-1
7
1
1
1
8
-1
1
1
9
1
-1
1
10
-1
1
-1
In this dataframe, B has two consecutive "1" in t=7 and t=8, and C has three consecutive "1" in t=7 to to=9.
Probability of event that two consecutive "1" appear is 3/27
Probability of event that three consecutive "1" appear is 1/24
How can I do this using python pandas?
Try this code(It can be used in other dataframes i.e. more columns, rows)
def consecutive(num):
'''
df = pd.DataFrame({
'Time' : [i for i in range(1, 11)],
'A' : [1, -1, 1, -1, 1, -1, 1, -1, 1, -1],
'B' : [1, -1, 1, -1, 1, -1, 1, 1, -1, 1],
'C' : [1, -1, 1, -1, 1, -1, 1, 1, 1, -1]
})
print(df)
'''
row_num = df.shape[0]
col_num = df.shape[1]
cnt = 0 # the number of consecutives
for col_index in range(1, col_num): # counting for each column
col_tmp = df.iloc[:, col_index]
consec = 0
for i in range(row_num):
if col_tmp[i] == 1:
consec += 1
# if -1 comes after 1, then consec = 0
else:
consec = 0
# to simply sum with the condition(consec == num), we minus 1 from consec
if consec == num:
cnt += 1
consec -= 1
all_cases = (row_num - num + 1) * (col_num - 1) # col_num - 1 because of 'Time' column
prob = cnt / all_cases
return prob
When you execute it with the given dataframe with this code
print(f'two consectuvie : {consecutive(2)}')
print(f'three consectuvie : {consecutive(3)}')
Output :
Time A B C
0 1 1 1 1
1 2 -1 -1 -1
2 3 1 1 1
3 4 -1 -1 -1
4 5 1 1 1
5 6 -1 -1 -1
6 7 1 1 1
7 8 -1 1 1
8 9 1 -1 1
9 10 -1 1 -1
two consectuvie : 0.1111111111111111
Time A B C
0 1 1 1 1
1 2 -1 -1 -1
2 3 1 1 1
3 4 -1 -1 -1
4 5 1 1 1
5 6 -1 -1 -1
6 7 1 1 1
7 8 -1 1 1
8 9 1 -1 1
9 10 -1 1 -1
three consectuvie : 0.041666666666666664
You can compare rows with previous rows using shift. So, to find out how often two consecutive values are equal, you can do
>>> (df.C == df.C.shift()).sum()
2
To find three consecutive equal values, you'd have to compare the column with itself shifted by 1 (the default) and additionally, shifted by 2.
>>> ((df.C == df.C.shift()) & (df.C == df.C.shift(2))).sum()
1
Another variation of this using the pd.Series.eq function instead of the == is:
>>> m = df.C.eq(df.C.shift(1)) & df.C.eq(df.C.shift(2))
>>> m.sum()
1
In this case, since the target value is 1 (and True == 1 is True; it won't work for other target values as is, see below), the pattern can be generalized with functools.reduce to:
from functools import reduce
def combos(column, n):
return reduce(pd.Series.eq, [column.shift(i) for i in range(n)])
You can apply this function to df like so, which will give you the numerator:
>>> df[['A', 'B', 'C']].apply(combos, n = 2).values.sum()
3
>>> df[['A', 'B', 'C']].apply(combos, n = 3).values.sum()
1
To get the denominator, you can do, e.g.,
n = 2
rows, cols = df[['A', 'B', 'C']].shape
denominator = (rows - n + 1) * cols
An idea for a generalized version of the combos function that should work with other target values is
from operator import and_ # equivalent of &
def combos_generalized(col, n):
return reduce(and_, [col == col.shift(i) for i in range(1, n)])

data frame and list operation

There are three columns in df: mins, maxs, and col. I would like to generate a binary list according to the following rule: if col[i] is smaller than or equal to mins[i], add a "1" to the list and keep adding "1" for each row until col[i+n] is greater than or equal maxs[i+n]. After reaching maxs[i+n], add "0" to the list for each row until finding the next row where col[i] is smaller than or equal to mins[i]. Repeat this entire process till going over all rows.
For example,
col mins maxs
2 1 6 (0)
4 2 6 (0)
2 3 7 (1)
5 5 6 (1)
4 3 8 (1)
4 2 5 (1)
5 3 5 (0)
4 0 5 (0)
3 3 8 (1)
......
So the list would be [0,0,1,1,1,1,0,0,1]. Does this make sense?
I gave it a shot and wrote the following, which unfortunately did not achieve what I wanted.
def get_list(col, mins, maxs):
l = []
i = 0
while i <= len(col):
if col[i] <= mins[i]:
l.append(1)
while col[i+1] <= maxs[i+1]:
l.append(1)
i += 1
break
break
return l
Thank you so much folks!
My answer may not be elegant but should work according to your expectation.
Import the pandas library.
import pandas as pd
Create dataframe according to data provided.
input_data = {
'col': [2, 4, 2, 5, 4, 4, 5, 4, 3],
'mins': [1, 2, 3, 5, 3, 2 , 3, 0, 3],
'maxs': [6, 6, 7, 6, 8, 5, 5, 5, 8]
}
dataframe_ = pd.DataFrame(data=input_data)
Using a for loop iterate over the rows. The switch variable will change accordingly depending on the conditions was provided which results in the binary column being populated.
binary_switch = False
for index, row in dataframe_.iterrows():
if row['col'] <= row['mins']:
binary_switch = True
elif row['col'] >= row['maxs']:
binary_switch = False
binary_output = 1 if binary_switch else 0
dataframe_.at[index, 'binary'] = binary_output
dataframe_['binary'] = dataframe_['binary'].astype('int')
print(dataframe_)
Output from code.
col mins maxs binary
0 2 1 6 0
1 4 2 6 0
2 2 3 7 1
3 5 5 6 1
4 4 3 8 1
5 4 2 5 1
6 5 3 5 0
7 4 0 5 0
8 3 3 8 1
Your rules give the following decision tree:
1: is col <= mins?
True: l.append(1)
False: next question
2: was col <= mins before?
False: l.append(0)
True: next question:
3: is col >= maxs?
True: l.append(0)
False: l.append(1)
Making this into a function with an if/else tree, you get this:
def make_binary_list(df):
l = []
col_lte_mins = False
for index, row in df.iterrows():
col = row["col"]
mins = row["mins"]
maxs = row["maxs"]
if col <= mins:
col_lte_mins = True
l.append(1)
else:
if col_lte_mins:
if col >= maxs:
col_lte_mins = False
l.append(0)
else:
l.append(1)
else:
l.append(0)
return l
make_binary_list(df) gives [0, 0, 1, 1, 1, 1, 0, 0, 1]

How to generate a combination of two numbers that equal a given input

I am trying to generate a combination of two numbers using only 5 and 7 that will equal the sum of any given input. The input range is 24:1000. What I've done so far is determine if the input is divisible by 5 or 7 and then generate a list whose sum equals the input.
Here is my working example:
def change(amount):
if amount not in range(24,1001):
print("The number is outside the range.")
else:
remainder=amount%5
if remainder==0:
n = int(amount/5)
array=[0 for i in range(n)]
for i in range(n):
array[i]=5
return array, sum(array)
else:
remainder=amount%7
if remainder==0:
n = int(amount/7)
array=[0 for i in range(n)]
for i in range(n):
array[i]=7
return array, sum(array)
else:
# here is where am stuck
print(change(28))
The output needs to be in the form of an array. For example: for change(24), the array should be [5,5,7,7].
Most simplistic approach I could think of
def change(target):
if target not in range(24,1001):
raise ValueError("The number is outside the range.")
res = []
while target % 5 != 0:
res.append(7)
target -= 7
while target != 0:
res.append(5)
target -= 5
return res
change(28)
>>> [7, 7, 7, 7]
You don't need loops for this, just a little bit of algebra. See the comments for details.
def change(amount):
if not 24 <= amount <= 1000:
print("The number is outside the range.")
return None
# 5*3 - 7*2 = 1, so 5*(3*n) + 7*(-2*n) = n, and therefore
# 5*(3*n - 7*k) + 7*(5*k - 2*n) = n for all k
# We want to find a small k so that the terms in each bracket are positive
k = 3 * amount // 7
x = 3 * amount - 7 * k
y = 5 * k - 2 * amount
return x, y, [5] * x + [7] * y
# Print the sequences for some small amounts
for i in range(24, 70):
x, y, seq = change(i)
print(i, x, y, sum(seq) == i)
# Check all values in the range
for i in range(24, 1001):
x, y, seq = change(i)
assert sum(seq) == i
print('ok')
output
24 2 2 True
25 5 0 True
26 1 3 True
27 4 1 True
28 0 4 True
29 3 2 True
30 6 0 True
31 2 3 True
32 5 1 True
33 1 4 True
34 4 2 True
35 0 5 True
36 3 3 True
37 6 1 True
38 2 4 True
39 5 2 True
40 1 5 True
41 4 3 True
42 0 6 True
43 3 4 True
44 6 2 True
45 2 5 True
46 5 3 True
47 1 6 True
48 4 4 True
49 0 7 True
50 3 5 True
51 6 3 True
52 2 6 True
53 5 4 True
54 1 7 True
55 4 5 True
56 0 8 True
57 3 6 True
58 6 4 True
59 2 7 True
60 5 5 True
61 1 8 True
62 4 6 True
63 0 9 True
64 3 7 True
65 6 5 True
66 2 8 True
67 5 6 True
68 1 9 True
69 4 7 True
ok
This doesn't necessarily give the shortest sequence for every amount, but it's usually close to the optimum, and it's not too hard to modify this code to get the optimum. But that will be left as an exercise for the reader. ;)
To learn more about this technique, please see the Wikipedia article on linear Diophantine equations.
This is the fastest I could think of, as it only uses a single while cycle.
def change(amount):
if not 24 <= amount <= 1001:
print('{} is outside range'.format(amount))
return
sevens = []
fives, remainder = divmod(amount, 5)
while remainder:
amount -= 7
sevens.append(7)
fives, remainder = divmod(amount, 5)
return [5] * fives + sevens
PS: please don't mix tabulations sizes :-)
For all combinations here is an idea. The loops can be optimise further
import itertools
def change(target):
nums = [5, 7]
min_len = int(target / 7)
max_len = int(target / 5) + 1
res = []
for len_ in range(min_len, max_len):
r = itertools.combinations_with_replacement(nums, len_)
for item in r:
if sum(item) == target:
res.append(item)
return res
And a little bit optimised one. Collect all combinations within the possible range and filter out the ones with sum == target.
import itertools
def change(target):
nums = [5, 7]
min_len = int(target / 7)
max_len = int(target / 5) + 1
res = []
for len_ in range(min_len, max_len):
res += (list(itertools.combinations_with_replacement(nums, len_)))
return list(filter(lambda x: sum(x) == target, res))
The output which contains all possible combinations for target = 70
[(7, 7, 7, 7, 7, 7, 7, 7, 7, 7), (5, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7), (5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5)]
Without any loop in FP style. Reducing all possible combinations into one list and filter the correct elements of this list in return.
import itertools
from functools import reduce
def change(target):
nums = [5, 7]
min_len = int(target / 7)
max_len = int(target / 5) + 1
res = reduce(lambda x, y:
x + (list(itertools.combinations_with_replacement(nums, y))),
range(min_len, max_len), [])
return list(filter(lambda x: sum(x) == target, res))

Using infinity in dynamic programming coins example - why does this not fail?

been trying to understand how to use dynamic programming on this page
https://www.topcoder.com/community/data-science/data-science-tutorials/dynamic-programming-from-novice-to-advanced/
Given a list of N coins, their values (V1, V2, … , VN), and the total sum S. Find the minimum number of coins the sum of which is S (we can use as many coins of one type as we want), or report that it’s not possible to select coins in such a way that they sum up to S.
Given coins with values 1, 3, and 5.
And the sum S is set to be 11.
Set Min[i] equal to Infinity for all of i
Min[0]=0
For i = 1 to S
For j = 0 to N - 1
If (Vj<=i AND Min[i-Vj]+1<Min[i])
Then Min[i]=Min[i-Vj]+1
Output Min[S]
I am confused as to why we are setting infinity for all of i.
even more confusing is when the sum is 1
If (Vj<=i AND Min[i-Vj]+1<Min[i])
wouldn't Min[1] be undefined? wouldn't the code fail here? and why are they adding +1??
or will it continue because is it infinite? Why are they using infinite here? And what is N-1 where did they get it from?
Overall I found their explanation very hard to follow.
This is hopefully a direct translation:
def dp_coin(S, coins):
# set all values to infinity in range S/sum needed
mn = [float("inf") for j in range(S+1)]
# takes 0 coins to sum 0
mn[0] = 0
# start at second index 1
for i in range(1, S+1):
for j in range(len(coins)):
if coins[j] <= i and mn[i-coins[j]]+1 < mn[i]:
mn[i] = mn[i-coins[j]] + 1
return mn[-1]
print(dp_coin(11, [1, 3, 5]))
3
If you print mn you will see:
[0, 1, 2, 1, 2, 1, 2, 3, 2, 3, 2, 3]
which is the same as the table:
Sum Min. nr. of coins Coin value added to a smaller sum to
obtain this sum (it is displayed in brackets)
0 0 -
1 1 1 (0)
2 2 1 (1)
3 1 3 (0)
4 2 1 (3)
5 1 5 (0)
6 2 3 (3)
7 3 1 (6)
8 2 3 (5)
9 3 1 (8)
10 2 5 (5)
11 3 1 (10)
S refers to the total sum required, coins[j] is equivalent to Vj, N was referring to coins.
The inner loop can be removed and simply iterate over coins:
def dp_coin(S, coins):
mn = [float("inf") for j in range(S+1)]
mn[0] = 0
for i in range(1, S+1):
for j in coins:
if j <= i and mn[i-j]+1 < mn[i]:
mn[i] = mn[i-j] + 1
return mn[-1]

Categories

Resources