I'm trying to optimize my solution for Hackerranks's 'New Year Chaos' problem. The gist of the problem goes like this
There's a queue of n people, labeled 1 through n, and each person can bribe the person directly in front of them to swap places and get closer to the front of the queue (in this case, index 0 of the list/array). Each person can only bribe a maximum of two times (and they cannot bribe someone who has already bribed them)
You are given the order of the people after all of the bribes have taken place and your job is to determine how many bribes took place to get to that point. For example, if you were given [3, 2, 1] then the answer would be 3 bribes (person 3 bribed person 1, 2 and person 2 bribed person 1).
My solution was, for each person I, count the number of people to the left of I that have a label greater than I (they would've had to bribe person I to get to the left of them). To complicate things (slightly), some of the test cases given would only be possible if someone bribed more than 2 times (i.e. [4, 1, 2, 3] - person 4 bribed person 3, then 2, then 1 to get to the front). If this is the case, simply output "Too chaotic"
Anyway here's the code:
# n is the number of people in the list
# q is the order of the people after the bribery has taken place ex. [1, 3, 2, 5, 4]
for I in range(1, n + 1): # for each person I in the list
index = q.index(I)
if I - index > 3: # more than two bribes
bribes = "Too chaotic"
break
for j in range(index): # for each number to the left of I, if greater than I, count it as a bribe
if q[j] > I:
bribes = bribes + 1
print bribes
My problem is that the code times out with some of the larger test cases (you're only given so much time for each test case to run). How can I optimize the algorithm so that it doesn't time out? Should I be trying this problem in another language?
An optimization to your solution would be to start the nested loop from q[i] - 2 instead of 0. Since no one can jump ahead of its original position by more than 2, so any value higher than q[i] can only be till index q[i] -2.
Something like:
for(int j = Math.max(0, q[i] - 2); j < i; j++) {
if(q[j] > q[i]) {
bribe++;
}
}
def minimumBribes(q):
bribes = 0
for i in range(len(q)-1,-1,-1):
if q[i] - (i + 1) > 2:
print('Too chaotic')
return
for j in range(max(0, q[i] - 2),i):
if q[j] > q[i]:
bribes+=1
print(bribes)
The final optimization is in the inner loop, to exclude all those people who were never in a position to offer the current person a bribe.
You already halt this loop when you reach the current persons final position because obviously no-one behind their final position gave them a bribe...
But what about all those people in front? This person could, at best, get to two positions in front of where they started, by issuing two bribes. But no-one in front of that was ever in a position to offer them a bribe, so we can exclude everyone further forward.
Thus the inner loop ranges from two spots in front of my start position to my final position. Which chops out a lot of iterations when the list gets lengthy.
def minimumBribes(q):
bribes = 0
for final_pos, start_pos in enumerate(q):
# Abort if anyone is more than two bribes ahead of where they started
if final_pos + 1 < start_pos - 2:
print('Too chaotic')
return
# Count the number of people who started behind me, who are ahead of my
# final position. Conduct the search between two spots forward of where
# I started, thru to the person in front of me in the end; as these are
# the only people to have potentially bribed me.
potential_bribers = range(max(start_pos - 2, 0), final_pos)
bribes += [q[briber] > start_pos for briber in potential_bribers].count(True)
print(bribes)
I may be able to work these puzzles out, but never ever can I do it within their timeframes. Which is why I didn't even bother an attempt the last time a potential employer put a hacker rank test in front of me. They can have the braniacs, the rest us mere mortals have stackoverflow.
Just came across this problem, took me some time but here's a nice clean solution that is optimized for larger test cases (and passes 10/10 on HackerRank). I realise it takes a slightly different approach to yours but thought I'd share as it is working nicely and might still help.
A key point that helped me is that for any given person X in the queue, the furthest you'll need to look for a person that overtook them is 1 spot in front of where person X started. People can be overtaken more than 2 times, so for example, person 1 can end up at the back of the queue even if the queue is 1000 people long. However, person Y can't end up more than two places ahead of where they began (otherwise they must have overtaken more than twice). This is the reason you can be sure you don't need to look further than 1 place in front of where person X began (2 places in front of the nearest possible overtaker) for the people that overtook them.
def minimumBribes(q):
count = 0
tooChaotic = False
for i in range(len(q)):
# to translate between q[i] and the position in a zero-indexed python list, you have to add 1, so here it's i+3 rather than i+2
if q[i] > i + 3:
print("Too chaotic")
tooChaotic = True
break
else:
# q[i]-2 rather than q[i]-1 since python is zero-indexed but the people in the queue start at 1
start = max(0, q[i]-2)
for j in range(start, i):
if q[j] > q[i]:
count += 1
if tooChaotic == False:
print(count)
There are two problems in your code.
First, you should iterate the given array from end to the beginning, not the other way around. The reason is that if you iterate from the beginning, you need to iterate the whole array in the inner loop. But if you iterate from the last element, you just need to check left two numbers of each number to count the inversion. I think this is why your code "time out" on large test cases.
Second, when you print out bribes at the last line, you should check if you "break out" from the outer loop, or it finished at i == n. Otherwise, it might print out "Too chaotic" and some bribes you already computed.
My Solution is better because it has less no of loops
count = 0
flag = True
i = len(q) - 1
for x in range(len(q)):
temp = max(q) - x
index = q.index(temp)
if index < i - x:
if i - x - index > 2:
print("Too chaotic")
flag = False
break
q.remove(temp)
q.insert(i - x, temp)
count += i - x - index
if flag:
print(count)
def minimumBribes(q):
moves = 0
for pos, val in enumerate(q):
if (val-1) - pos > 2:
return "Too chaotic"
for j in xrange(max(0,val-2), pos):
if q[j] > val:
moves+=1
return moves
Here is a complete C++ solution, based on #lazywiz's comment:
void minimumBribes(vector<int> q) {
int numBribes{0};
bool chaotic = false;
vector<bool> visited(100000, false);
for (int i=0; i<q.size(); i++) {
int pos = i+1;
if (q[i] - pos > 2) {
chaotic = true;
break;
} else if (q[i] <= pos) {
for (int j=q[i]+1; j<=pos+3; j++) {
if (visited[j-1]) { numBribes++; }
}
}
visited[q[i]-1] = true;
}
if (chaotic) {
cout << "Too chaotic\n";
} else {
cout << numBribes << "\n";
}
}
def minimumBribes(q):
total_bribe=0
chaos=""
for i in range(0, len(q)):
if chaos == "Too chaotic":
break
bribe_count=0
for j in range(i, len(q)):
if q[i]>q[j]:
if bribe_count<2:
bribe_count+=1
else:
chaos="Too chaotic"
break
total_bribe+=bribe_count
if chaos=="":
print(total_bribe)
else:
print(chaos)
How more can we optimize this code?
Related
This is for a school assignment.
I have been tasked to define a function determining the largest square pyramidal number up to a given integer(argument). For some background, these are square pyramidal numbers:
1 = 1^2
5 = 1^2+2^2
14 = 1^2+2^2+3^2
So for a function and parameter largest_square_pyramidal_num(15), the function should return 14, because that's the largest number within the domain of the argument.
I get the idea. And here's my code:
def largest_square_pyramidal_num(n):
sum = 0
i = 0
while sum < n:
sum += i**2
i += 1
return sum
Logically to me, it seemed nice and rosy until I realised it doesn't stop when it's supposed to. When n = 15, sum = 14, sum < n, so the code adds one more round of i**2, and n is exceeded. I've been cracking my head over how to stop the iteration before the condition sum < n turns false, including an attempt at break and continue:
def largest_square_pyramidal_num(n):
sum = 0
for i in range(n+1):
sum += i**2
if sum >= n:
break
else:
continue
return sum
Only to realise it doesn't make any difference.
Can someone give me any advice? Where is my logical lapse? Greatly appreciated!
You can do the following:
def largest_pyr(x):
pyr=[sum([i**2 for i in range(1,k+1)]) for k in range(int(x**0.5)+1)]
pyr=[i for i in pyr if i<=x]
return pyr[-1]
>>>largest_pyr(15)
14
>>> largest_pyr(150)
140
>>> largest_pyr(1500)
1496
>>> largest_pyr(15000)
14910
>>> largest_pyr(150000)
149226
Let me start by saying that continue in the second code piece is redundant. This instruction is used for scenario when you don't want the code in for loop to continue but rather to start a new iteration (in your case there are not more instructions in the loop body).
For example, let's print every number from 1 to 100, but skip those ending with 0:
for i in range(1, 100 + 1):
if i % 10 != 0:
print(i)
for i in range(1, 100 + 1):
if i % 10 == 0:
# i don't want to continue executing the body of for loop,
# get me to the next iteration
continue
print(i)
The first example is to accept all "good" numbers while the second is rather to exclude the "bad" numbers. IMHO, continue is a good way to get rid of some "unnecessary" elements in the container rather than writing an if (your code inside if becomes extra-indented, which worsens readability for bigger functions).
As for your first piece, let's think about it for a while. You while loop terminates when the piramid number is greater or equal than n. And that is not what you really want (yes, you may end up with a piramid number which is equal to n, but it is not always the case).
What I like to suggest is to generate a pyramid number until in exceedes n and then take a step back by removing an extra term:
def largest_square_pyramidal_num(n):
result = 0
i = 0
while result <= n:
i += 1
result += i**2
result -= i ** 2
return result
2 things to note:
don't use sum as a name for the variable (it might confuse people with built-in sum() function)
I swapped increment and result updating in the loop body (such that i is up-to-date when the while loop terminates)
So the function reads like this: keep adding terms until we take too much and go 1 step back.
Hope that makes some sense.
Cheers :)
I m unable to understand what is logic behind the solution for Codility FrogRiverOne here https://codility.com/demo/take-sample-test/frog_river_one
Task description
A small frog wants to get to the other side of a river. The frog is initially located on one bank of the river (position 0) and wants to get to the opposite bank (position X+1). Leaves fall from a tree onto the surface of the river.
You are given an array A consisting of N integers representing the falling leaves. A[K] represents the position where one leaf falls at time K, measured in seconds.
The goal is to find the earliest time when the frog can jump to the other side of the river. The frog can cross only when leaves appear at every position across the river from 1 to X (that is, we want to find the earliest moment when all the positions from 1 to X are covered by leaves). You may assume that the speed of the current in the river is negligibly small, i.e. the leaves do not change their positions once they fall in the river.
For example, you are given integer X = 5 and array A such that:
A[0] = 1
A[1] = 3
A[2] = 1
A[3] = 4
A[4] = 2
A[5] = 3
A[6] = 5
A[7] = 4
In second 6, a leaf falls into position 5. This is the earliest time when leaves appear in every position across the river.
Write a function:
def solution(X, A)
that, given a non-empty array A consisting of N integers and integer X, returns the earliest time when the frog can jump to the other side of the river.
If the frog is never able to jump to the other side of the river, the function should return −1.
For example, given X=5 and array A such that:
A[0] = 1
A[1] = 3
A[2] = 1
A[3] = 4
A[4] = 2
A[5] = 3
A[6] = 5
A[7] = 4
the function should return 6, as explained above.
Assume that:
N and X are integers within the range [1 . 100,000]
each element of array A is an integer within the range (1 . X).
Complexity:
expected worst-case time complexity is O(N);
expected worst-case space complexity is O(X) (not counting the storage required for input arguments).
Solution--
Input arguments to Function - (2,[2,2,2,2,2]) and (5, [1, 3, 1, 4, 2, 3, 5, 4])
def solution(X,A):
covered = 0
covered_a = [-1]*X
for index,element in enumerate(A):
if covered_a[element-1] == -1:
covered_a[element-1] = element
covered += 1
if covered == X:
return index
return -1
I want to understand what is the logic behind creating an boolean array and substracting 1 element wise from the input array A
It's because you want to "flag" all the numbers you've seen so far. So it begins with all of them on 'False' because you haven't seen any of them yet.
I'm going to give you my Java solution to this question which scored 100%. The main strategy is to use java.util.Set to store all required integers for a full jump and a second java.util.Set to keep storing current leaves and to keep checking if the first set fully exists in the second set.
package com.codility.lesson04.countingelements;
import java.util.HashSet;
import java.util.Set;
public class FrogRiverOne {
public int solution(int X, int[] A) {
SetrequiredLeavesSet = new HashSet();
for(int i=1; i<=X; i++) {
requiredLeavesSet.add(i);
}
SetcurrentLeavesSet = new HashSet();
for(int p=0; p<A.length; p++) {
currentLeavesSet.add(A[p]);
//keep adding to current leaves set until it is at least the same size as required leaves set
if(currentLeavesSet.size() < requiredLeavesSet.size()) continue;
if(currentLeavesSet.containsAll(requiredLeavesSet)) {
return p;
}
}
return -1;
}
}
You can find the code and unit tests for this problem here and an entire list of Codility solutions with explanations of the strategies here.
This is the best solution that I came up with and is very easy to understand. It gives O(n) time complexity.
def solution(X, A):
positions = set()
seconds = 0
for i in range(0, len(A)):
if A[i] not in positions and A[i] <= X:
positions.add(A[i])
seconds = i
if len(positions) == X:
return seconds
return -1
I couldn't find an answer by searching and I've been working on this for two days and am stumped. I think I am just confused on the math. I am trying to write a function that finds the first n triangular numbers.
def formula(n):
i = 1
while i <= n:
print i, '\t', n * (n + 1)/ 2
i += 1
print
So for example if I type in formula(5) it would look like this:
1 1
2 3
3 6
4 10
5 15
I got how to make a table going from 1 through whatever number I choose n.. but I can't figure out how to make the second part equal the formula I typed in which would be n*(n+1)/2. What is the logic going through this? Everytime I type in formula(5) for example the loop goes from 1-5 but returns the same exact answer in the right hand column all the way down which would be 15. I can't make it to where it would start at 1, 3, 6 etc on the right hand side.
The comment that observed that you are computing n * (n + 1) / 2 instead of i * (i + 1) / 2 is correct. You can also get rid of having to do a multiplication and division at each step by observing that the i-th triangle number is simply the sum of the integers from 1 to i, so at each step you just have to add i to the previous triangle number. Code below:
def formula(n):
ith_sum = 0
for i in xrange(1, n+1):
ith_sum += i
print i, '\t', ith_sum
I had completely forgotten about triangular numbers, thank you for the question! I am glad you now know the correct solution. I was curious if it could be done a different way:
def triangular(number):
return number + triangular(number-1) if number else 0
print triangular(5)
If you fancy a challenge, you could try and work this out. A print statement here and there would help you spot what is happening.
This is a simple solution that worked for me
def print_triangular_numbers(n):
for i in range(n+1):
print(i, int(i * (i + 1)/ 2))
print_triangular_numbers(5)
This is not the answer to this question. So delete it as soon as you read it.
It is the answer for printing
#
##
###
####
#####
######
on the console.
Follwing is the code for it :
var strng="";
for (var i=1; i<7; i++)
{
for (var j=1; j<=i; j++)
{
strng=strng+"#";
}
strng=strng+"\n";
}
console.log(strng);
So I was attacking a Euler Problem that seemed pretty simple on a small scale, but as soon as I bump it up to the number that I'm supposed to do, the code takes forever to run. This is the question:
The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17.
Find the sum of all the primes below two million.
I did it in Python. I could wait a few hours for the code to run, but I'd rather find a more efficient way to go about this. Here's my code in Python:
x = 1;
total = 0;
while x <= 2000000:
y = 1;
z = 0;
while x >= y:
if x % y == 0:
z += 1;
y += 1;
if z == 2:
total += x
x += 1;
print total;
Like mentioned in the comments, implementing the Sieve of Eratosthenes would be a far better choice. It takes up O(n) extra space, which is an array of length ~2 million, in this case. It also runs in O(n), which is astronomically faster than your implementation, which runs in O(n²).
I originally wrote this in JavaScript, so bear with my python:
max = 2000000 # we only need to check the first 2 million numbers
numbers = []
sum = 0
for i in range(2, max): # 0 and 1 are not primes
numbers.append(i) # fill our blank list
for p in range(2, max):
if numbers[p - 2] != -1: # if p (our array stays at 2, not 0) is not -1
# it is prime, so add it to our sum
sum += numbers[p - 2]
# now, we need to mark every multiple of p as composite, starting at 2p
c = 2 * p
while c < max:
# we'll mark composite numbers as -1
numbers[c - 2] = -1
# increment the count to 3p, 4p, 5p, ... np
c += p
print(sum)
The only confusing part here might be why I used numbers[p - 2]. That's because I skipped 0 and 1, meaning 2 is at index 0. In other words, everything's shifted to the side by 2 indices.
Clearly the long pole in this tent is computing the list of primes in the first place. For an artificial situation like this you could get someone else's list (say, this one), prase it and add up the numbers in seconds.
But that's unsporting, in my view. In which case, try the sieve of atkin as noted in this SO answer.
The question:
Given N integers [N<=10^5], count the total pairs of integers that have a difference of K. [K>0 and K<1e9]. Each of the N integers will be greater than 0 and at least K away from 2^31-1 (Everything can be done with 32 bit integers).
1st line contains N & K (integers).
2nd line contains N numbers of the set. All the N numbers are assured to be distinct.
Now the question is from hackerrank. I got a solution for the question but it doesn't satisfy the time-limit for all the sample test cases. I'm not sure if its possible to use another algorithm but I'm out of ideas. Will really appreciate if someone took a bit of time to check my code and give a tip or two.
temp = input()
temp = temp.split(" ")
N = int(temp[0])
K = int(temp[1])
num_array = input()
num_array = num_array.split(" ")
diff = 0
pairs= 0
i = 0
while(i < N):
num_array[i] = int(num_array[i])
i += 1
while(num_array != []):
j = 0
while(j < (len(num_array)-1)):
diff = abs(num_array[j+1] - num_array[0])
if(diff == K):
pairs += 1
j += 1
del num_array[0]
if(len(num_array) == 1):
break
print(pairs)
You can do this in aproximately linear time by following the procedure:
So, O(n) solution:
For each number x add it to hash-set H[x]
For each number x check whether x-k is in H, if yes - add 1 to answer
Or by using some balanced structure (like tree-based set) in O(nlgn)
This solution bases on the assumption that integers are distinct, if they are not you need to store the number of times element has been "added to set" and instead of adding 1 to answer - add the product of H[x]*H[x+k]
So in general you take some HashMap H with "default value 0"
For each number x update map: H[x] = H[x]+1
For each number x add to answer H[x]*H[x-k] (you don't have to check whether it is in the map, because if it is not, H[x-k]=0 )
and again - solution using hash-map is O(n) and using tree-map O(nlgn)
So given set of numbesr A, and number k (solution for distinct numbers):
H=set()
ans=0
for a in A:
H.add(a)
for a in A:
if a-k in H:
ans+=1
print ans
or shorter
H=set(A)
ans = sum(1 for a in A if a-k in H)
print ans
Use a dictionary (hash map).
Step 1: Fill the dictionary D with all entries from the array.
Step 2: Count occurences of all A[i] + k in the dictionary.
Dictionary<int> dict = new Dictionary<int>();
foreach (int n in num_array) do dict.Add(n);
int solitions = 0;
foreach (int n in num_Array) do
if dict.contains(n+k)
solutions += 1;
Filling a dictionary is O(1), Searching is O(1) as well. Doing it for each element in the array is O(n). This is as fast as it can get.
Sorry, you have to translate it to python, though.
EDIT: Same idea as the previous one. Sorry to post a duplicate. It's too late to remove my duplicate I guess.