Number of seating arrangement following social distancing guidelines - python-3.x

Came across this interesting problem and trying to come up with algorithm.
I was able to write a backtracking solution:
def get_possible_seatings(seats, dist):
# Account for the last seat
seats.append(0)
arrangement = []
total_seatings = 0
def dfs(curr, prev_dist):
nonlocal total_seatings
if curr > len(seats):
return
if curr == len(seats):
total_seatings += 1
return
# We have only one choice, don't sit
if prev_dist < dist:
dfs(curr+1, prev_dist+seats[curr])
else:
# We have 2 choices here
arrangement.append(curr)
dfs(curr+1, seats[curr]) # We do sit
arrangement.pop(-1)
dfs(curr+1, prev_dist+seats[curr]) # We don't sit
return
for index in range(len(seats)):
arrangement.clear()
arrangement.append(index)
dfs(index + 1, seats[index])
# Account for no seats occupied
return total_seatings + 1
And my own version of the dynamic-programming solution:
def get_possible_seatings(seats, distance):
"""
This is O(n^2) solution
"""
# Account for the last seat
seats.append(0)
# Each seat can be occupied individually
dp = [1] * len(seats)
# Keep track of total distance from first seat
total_distance = [0] * len(seats)
prefix_sum = seats[0]
for index, i in enumerate(seats[1:], 1):
total_distance[index] = prefix_sum
prefix_sum += i
# Start from second seat onwards, this is the curr seat 'i'
for i in range(1, len(seats)):
"""
Check each of the seat starting from 1st. If the distance between current seat 'i' and previous
seat 'j' is desirable, add the dp at previous seat 'j' to current dp
This basically means, that both previous seat and current seat can be occupied together then
total number of ways to seat will increase by number of ways to be seated at previous seat
"""
for j in range(i):
if total_distance[i] - total_distance[j] >= distance:
dp[i] += dp[j]
# Account for no seat occupied
return sum(dp) + 1
But I am unable to wrap my head around the O(n) solution posted in the original link
Could not figure out the logic without comments :(
My take is:
It looks like sliding window technique where we keep left and right prefix sums. And keep expanding window(right) till we hit the desired distance. Then keep shrinking it(left) as long as we are at distance >= desired distance.
Can anyone help me confirm this, please?

Related

Given a String of Buckets (alphabets). Find the cost (possibly minimal) to bring all the buckets at the base

Bob is a construction worker who does mathematics for increasing his efficiency. He is working on a site and has n buckets of cement-lined up with different characters (a – z) marked upon them. He has a strict command from the senior that he cannot change the order of the buckets.
Before starting his work, he has been given a string s of length n in which the character at position i (1 <= i <= n) gives us the mark on the i'th bucket. He can only carry one bucket at a time and bring it back to the base site. In each round, he has a criterion on which bucket to pick up. He will take the bucket with the smallest character marked upon it (a<b<z) and if there are multiple buckets with the smallest character, then he will take the one closest to the base.
The cost of picking up a bucket B is the number of buckets he passes through while walking from the site to get B (the bucket which he picks is also included). In each round, the cost accumulates. Find the final cost incurred by Bob while completing his job.
Constraints
1 < t,m < 10^5
The sum of n over all test cases does not exceed 10^6
SAMPLE INPUT
2
badce
SAMPLE OUTPUT
7
Explanation
badce - Firstly Bob takes the second basket with mark 'a' and adds 2 to the cost.
bdce - Then he takes the first basket with the mark 'b' and adds 1 to the cost.
dce - Then he takes the second basket with the mark 'c' and adds 2 to the cost.
de - Again he takes the first basket with the mark 'd' and adds 1 to the cost.
e - Again he takes the first basket with the mark 'e' and adds 1 to the cost.
The total cost becomes 7 units.
I have tried to code in Python but giving TLE for some cases.
Here is my approach-->
n = int(input())
s = input()
count_array = [0] * 26
for i in range(n):
count_array[ord(s[i])-97] += 1
alphabets = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
ans = 0
for i in range(26):
while count_array[i] > 0:
idx = s.index(alphabets[i])
ans += idx + 1
if idx > -1: s = s[0:idx] + s[idx+1:]
count_array[i] -= 1
print(ans)
I am looking for an optimized approach that takes O(nlogn) or O(n) time complexity. Thank You.
This runs in O(n). For every char, check how many previous chars will be transported later.
def get_cost(s):
result = 0
seen = [0] * 26
for c in s:
idx = ord(c) - ord('a')
result += 1 + sum(seen[idx+1:])
seen[idx] += 1
return result

Remove elements while in a for loop

I have a simpel Card Game, which I am currently working on for my thesis.
The Rules are simpel. You have a deck of 52 Cards, from 1 to 10 and jack, queen, knight.
You draw a card from your Deck. If its a Number it gets added to your Account. If you draw a jack, queen or knight, your account gets reset to 0. After every draw you can decide if you want to draw again or stop.
For this game, i programmed a code with the help of this site.
It should give the probability, that you draw exactly "target".
So for example, the probability to draw, so that you have 1 Point in your account,
is 4/52, since you have four 1´s. The Programm does give me exactly this value.
But. The probabiltity, that you have exactly 2 points in your account is
4/52 + 4/52*3/51. You can either draw a 2 with prob of 4/52 or a 1 and another 1 with prob 4/52*3/51.
Here the code messes up. It calculates the probability to have exactly 2 points in your account as
4/52 + 4/52*4/51 and i dont get why?
Can anyone help me?
import collections
import numpy as np
def probability(n, s, target):
prev = {0: 1} # previous roll is 0 for first time
for q in range(n):
cur = collections.defaultdict(int) # current probability
for r, times in prev.items():
cards = [card for card in range(1, 11)] * 4
for i in cards[:]:
cards.remove(i)
# if r occurred `times` times in the last iteration then
# r+i have `times` more possibilities for the current iteration.
cur[r + i] += times
prev = cur # use this for the next iteration
return (cur[t]*np.math.factorial(s-n)) / (np.math.factorial(s))
if __name__ == '__main__':
s = 52
for target in range(1, 151):
prob = 0
for n in range(1, 52):
prob += probability(n, s, target)
print(prob)
EDIT: I am fairly sure, that the line
for i in [i for i in cards]:
is the problem. Since cards.remove(i) removes the drawn card, but i doesnt care and can draw it anyway.
EDIT 2: Still searching. I tried the suggestions in this two qestions
How to remove list elements in a for loop in Python?
and
How to remove items from a list while iterating?
Nothing worked so far as it should.
I'm assuming with probability(n, s, target) you want to calculate the probability if you draw exactly n out of s cards that the sum of values is exactly target.
Then you will have a problem with n>=2. If I understand this right, for every iteration in the loop
for q in range(n):
you save in cur[sum] the number of ways to reach sum after drawing one card (p=0), two cards (p=1) and so on. But when you set p=1 you don't "remember" which card you have already drawn as you set
cards = [i for i in range(1, 11)] * 4
afterwards. So if you have drawn a "1" first (four possibilities) you have again still four "1"s you can draw out of your deck, which will give you your 4/52*4/51.
As a side note:
Shouldn't there be some kind of check if i==11 since that should reset your account?
I have solved it. After like a 4 Days.
This is the Code:
import numpy as np
def probability(cards, target, with_replacement = False):
x = 0 if with_replacement else 1
def _a(idx, l, r, t):
if t == sum(l):
r.append(l)
elif t < sum(l):
return
for u in range(idx, len(cards)):
_a(u + x, l + [cards[u]], r, t)
return r
return _a(0, [], [], target)
if __name__ == '__main__':
s = 52 # amount of cards in your deck
cards = [c for c in range(1, 11)] * 4
prob = 0
for target in range(1, 151): # run till 150 points
prob = probability(cards, target, with_replacement = False)
percentage = 0
for i in range(len(prob)):
percentage += np.math.factorial(len(prob[i])) * np.math.factorial(s-len(prob[i]))/(np.math.factorial(s))
print(percentage)
This Code is the Solution to my Question. Therefore this Thread can be closed.
For those who want to know, what it does as a tl;dr version.
You have a List (in this case Cards). The Code gives you every possible Combination of Elements in the List such as the Sum over the elements equals the target Value. Furthermore it also gives the Probability in the above mentioned Cardgame to draw a specific Value. The above mentioned game is basically the pig dice game but with cards.

I want to minimize the runtime of this Python program

Code that I want to minimize the runtime, it goes through an array of number and finds the max between the current max_product and the next product.
def max_pairwise_product(numbers):
n = len(numbers)
max_product = 0
for i in range(n):
for j in range(i+1,n):
max_product = max(max_product,numbers[i]*numbers[j])
return max_product
if __name__ == '__main__':
input_n = int(input())
input_numbers = [int(x) for x in input().split()]
print(max_pairwise_product(input_numbers))
Your code is trying to find the maximum product of any two non-identical elements of a numeric array. You are currently doing that by calculating each product. This algorithm has n²/2 calculations and comparisons, while all you actually need to do is much less:
We know from basic math that the two largest numbers in the original array will have the largest product. So all you need to do is:
Find the two largest integers in the array
multiply them.
You could do so by sorting the original array or just skimming through the array to find the two largest elements (which is a bit more tricky as it sounds because those two elements could have the same value but may not be the same element)
As a side note: In the future, please format your posts so that a reader may actually understand what your code does without going through hoops.
Sorting the numbers and multiplying the last two elements would give better time complexity than O(n^2).
Sort - O(nlogn)
Multiplication - O(1)
def max_pairwise_product(numbers):
n = len(numbers)
max_product = 0
numbers.sort()
if ((numbers[n-1] >0) and (numbers[n-2] >0)):
max_product = numbers[n-1]*numbers[n-2]
return max_product
if __name__ == '__main__':
input_n = int(input())
input_numbers = [int(x) for x in input().split()]
print(max_pairwise_product(input_numbers))

How can I reduce the time complexity of the given python code?

I have this python program which computes the "Square Free Numbers" of a given number. I'm facing problem regarding the time complexity that is I'm getting the error as "Time Limit Exceeded" in an online compiler.
number = int(input())
factors = []
perfectSquares = []
count = 0
total_len = 0
# Find All the Factors of the given number
for i in range(1, number):
if number%i == 0:
factors.append(i)
# Find total number of factors
total_len = len(factors)
for items in factors:
for i in range(1,total_len):
# Eleminate perfect square numbers
if items == i * i:
if items == 1:
factors.remove(items)
count += 1
else:
perfectSquares.append(items)
factors.remove(items)
count += 1
# Eleminate factors that are divisible by the perfect squares
for i in factors:
for j in perfectSquares:
if i%j == 0:
count +=1
# Print Total Square Free numbers
total_len -= count
print(total_len)
How can I reduce the time complexity of this program? That is how can I reduce the for loops so the program gets executed with a smaller time complexity?
Algorithmic Techniques for Reducing Time Complexity(TC) of a python code.
In order to reduce time complexity of a code, it's very much necessary to reduce the usage of loops whenever and wherever possible.
I'll divide your code's logic part into 5 sections and suggest optimization in each one of them.
Section 1 - Declaration of Variables and taking input
number = int(input())
factors = []
perfectSquares = []
count = 0
total_len = 0
You can easily omit declaration of perfect squares, count and total_length, as they aren't needed, as explained further. This will reduce both Time and Space complexities of your code.
Also, you can use Fast IO, in order to speed up INPUTS and OUTPUTS
This is done by using 'stdin.readline', and 'stdout.write'.
Section 2 - Finding All factors
for i in range(1, number):
if number%i == 0:
factors.append(i)
Here, you can use List comprehension technique to create the factor list, due to the fact that List comprehension is faster than looping statements.
Also, you can just iterate till square root of the Number, instead of looping till number itself, thereby reducing time complexity exponentially.
Above code section guns down to...
After applying '1' hack
factors = [for i in range(1, number) if number%i == 0]
After applying '2' hack - Use from_iterable to store more than 1 value in each iteration in list comprehension
factors = list( chain.from_iterable(
(i, int(number/i)) for i in range(2, int(number**0.5)+1)
if number%i == 0
))
Section 3 - Eliminating Perfect Squares
# Find total number of factors
total_len = len(factors)
for items in factors:
for i in range(1,total_len):
# Eleminate perfect square numbers
if items == i * i:
if items == 1:
factors.remove(items)
count += 1
else:
perfectSquares.append(items)
factors.remove(items)
count += 1
Actually you can completely omit this part, and just add additional condition to the Section 2, namely ... type(i**0.5) != int, to eliminate those numbers which have integer square roots, hence being perfect squares themselves.
Implement as follows....
factors = list( chain.from_iterable(
(i, int(number/i)) for i in range(2, int(number**0.5)+1)
if number%i == 0 and type(i**0.5) != int
))
Section 4 - I think this Section isn't needed because Square Free Numbers doesn't have such Restriction
Section 5 - Finalizing Count, Printing Count
There's absolutely no need of counter, you can just compute length of factors list, and use it as Count.
OPTIMISED CODES
Way 1 - Little Faster
number = int(input())
# Find Factors of the given number
factors = []
for i in range(2, int(number**0.5)+1):
if number%i == 0 and type(i**0.5) != int:
factors.extend([i, int(number/i)])
print([1] + factors)
Way 2 - Optimal Programming - Very Fast
from itertools import chain
from sys import stdin, stdout
number = int(stdin.readline())
factors = list( chain.from_iterable(
(i, int(number/i)) for i in range(2, int(number**0.5)+1)
if number%i == 0 and type(i**0.5) != int
))
stdout.write(', '.join(map(str, [1] + factors)))
First of all, you only need to check for i in range(1, number/2):, since number/2 + 1 and greater cannot be factors.
Second, you can compute the number of perfect squares that could be factors in sublinear time:
squares = []
for i in range(1, math.floor(math.sqrt(number/2))):
squares.append(i**2)
Third, you can search for factors and when you find one, check that it is not divisible by a square, and only then add it to the list of factors.
This approach will save you all the time of your for items in factors nested loop block, as well as the next block. I'm not sure if it will definitely be faster, but it is less wasteful.
I used the code provided in the answer above but it didn't give me the correct answer. This actually computes the square free list of factors of a number.
number = int(input())
factors = [
i for i in range(2, int(number/2)+1)
if number%i == 0 and int(int(math.sqrt(i))**2)!=i
]
print([1] + factors)

New to Python 3 - Trying to get results to print in table

I am new to Python and am having issues getting information to print in a table once 'n' or 'N'is entered. The code is below. I am probably missing something simple but have not been able to figure it out. Any help is appreciated.
Here is the result. As you can see, even after N is printed, it continues to ask for speed input:
Enter the speed in MPH.50
Enter the time travelled in hours.3
Do you have another calculation to enter? (Enter y for yes or N for no: )y
Enter the speed in MPH.60
Enter the time travelled in hours.4
Do you have another calculation to enter? (Enter y for yes or N for no: )y
Enter the speed in MPH.75
Enter the time travelled in hours.3
Do you have another calculation to enter? (Enter y for yes or N for no: )n
Enter the speed in MPH.
' # This program will calculate the distance a vehicle has travelled
# in miles using speed (mph) and the number of hours travelled.
# Create a variable to represent the maxium travel time in hours.
max_travel = 9
min_travel = 1
# Create a variable to represent the maximum speed.
max_speed = 120
# Create a variable to represent the minimum speed.
min_speed = 1
#Define a variable to represent continuation of the program
another = ["y", "Y"]
# Create a variable for saved results to be printed in table
results = []
# main module
def main():
# Get the speed in MPH.
speed = int(input('Enter the speed in MPH.'))
# Validate that the speed entered is not out of range.
while speed > max_speed or speed < min_speed:
if speed > max_speed:
print('ERROR: the speed entered must be a lower number between 1 and 120.')
speed = int(input('Enter a lower speed in MPH.'))
if speed < min_speed:
print('ERROR: the speed entered must be a higher number between 1 and 120.')
speed = int(input('Enter a higher speed in MPH.'))
# Ask user to input travel time in hours
travel_time = int(input('Enter the time travelled in hours.'))
# Validate that the time travelled is within the range of 1 to 9 hours.
while travel_time > max_travel or travel_time < min_travel:
if travel_time > max_travel:
print('ERROR: the time must be a lower number between 1 and 9.')
travel_time = int(input('Enter a different time travelled in hours.'))
# Validate that the time travelled is within the range of 1 to 9 hours.
if travel_time < min_travel:
print('ERROR: the time must be a higher number between 1 and 9.')
travel_time = int(input('Enter a different time travelled in hours.'))
# This will cause the loop, with the exception of the first loop,
# to depend on a 'y' or 'Y' entry to enter any additional entries.
first = False
# Calculate again?
another = input('Do you have another calculation to enter? ' + \
'(Enter y for yes or N for no: )')
# This loop will continue until something other
# than 'y' or 'Y' is entered when prompted.
while another == 'y' or 'Y':
main()
# Calculations saved for table
input_tuple =speed, travel_time
# Print the column headings in a table.
# Print the time travelled and the
# result of the distance calculation
print()
print('Hours\t\tDistance')
print('---------------------')
# Print saved entries and calculations in table
if another !='y' or 'Y':
for input_tuple in results:
# Calculate the distance travelled
distance = speed * travel_time
print("'%s'\t\t'%s'" %(travel_time, distance))
# Call the main function.
def main():
"""
"""'
Beware that the code comes with many indentation errors. Having said that you should at least correct
while another == 'y' or 'Y'
to
while another == 'y' or another == 'Y'
indeed or 'Y' always evaluates to True

Resources