3

Hello fellow stackoverflowers, I am practising my Python with an example question given to me (actually a Google interview practice question) and ran into a problem I did not know how to a) pose properly (hence vague title), b) overcome.

The question is: For an array of numbers (given or random) find unique pairs of numbers within the array which when summed give a given number. E.G: find the pairs of numbers in the array below which add to 6.

[1 2 4 5 11] 

So in the above case:

[1,5] and [2,4]

The code I have written is:

from secrets import *

i = 10

x = randbelow(10)

number = randbelow(100) #Generate a random number to be the sum that we are after#

if number == 0:
        pass
else:
        number = number

array = []

while i>0: #Generate a random array to use#
        array.append(x)
        x = x + randbelow(10)
        i -= 1

print("The following is a randomly generated array:\n" + str(array))
print("Within this array we are looking for a pair of numbers which sum to " + str(number))

for i in range(0,10):
        for j in range(0,10):
                if i == j or i>j:
                        pass
                else:
                        elem_sum = array[i] + array[j]
                        if elem_sum == number:
                                number_one = array[i]
                                number_two = array[j]
                                print("A pair of numbers within the array which satisfy that condition is: " + str(number_one) + " and " + str(number_two))
                        else:
                                pass

If no pairs are found, I want the line "No pairs were found". I was thinking a try/except, but wasn't sure if it was correct or how to implement it. Also, I'm unsure on how to stop repeated pairs appearing (unique pairs only), so for example if I wanted 22 as a sum and had the array:

[7, 9, 9, 13, 13, 14, 23, 32, 41, 45]

[9,13] would appear twice

Finally forgive me if there are redundancies/the code isn't written very efficiently, I'm slowly learning so any other tips would be greatly appreciated!

Thanks for reading :)

  • 1. will be easier for people to help you if you fix the indentation in the code 2. an easy way to accomplish this would be to create a variable called `found` and set it to `False` - then, if your `if elem_sum == number:` is triggered, write a line of code that changes `found` to `True` – n1c9 Jul 24 '17 at 18:33
  • @n1c9 *"fix the indentation"* isn't very helpful, the indentation is perfectly valid – Nick is tired Jul 24 '17 at 18:38
  • A little improvement here: instead of checking `i == j or i>j`, you can probably use `for j in range(j+1, 10)`. Some even better solutions: 1. Sort it and use two pointers (one from the beginning and one from the end), time complexity O(nlogn). 2. Use a dictionary, store `target - currentValue` as key. O(n) – Bubble Bubble Bubble Gut Jul 24 '17 at 18:43
  • @Ding Yes this is how I thought about it initially, but didn't know how to approach in Python. Thanks for the pointers though, I will have another look at doing this problem differently! –  Jul 24 '17 at 18:52

3 Answers3

0

You can simply add a Boolean holding the answer to "was at least one pair found?".

initialize it as found = false at the beginning of your code.

Then, whenever you find a pair (the condition block that holds your current print command), just add found = true.

after all of your search (the double for loop`), add this:

if not found:
    print("No pairs were found")
Ori
  • 1,680
  • 21
  • 24
  • This was a very simple solution, and I learned something new. Thank you! –  Jul 24 '17 at 18:44
  • Gladly. Also, I know this was not part of your original question, but you can also make the part of creating a list of X random numbers shorter and easier to understand (if you understand the following syntax): `random_nums = [randbelow(10) for num in range(10)]`. This essentially means that you want to create a list, where you iterate over each element of `range(10)` (in this case, this just means iterate 10 times), then, on each iteration, perform `randbelow(10)`. – Ori Jul 24 '17 at 18:56
0

Instead of actually comparing each pair of numbers, you can just iterate the list once, subtract the current number from the target number, and see if the remainder is in the list. If you convert the list to a set first, that lookup can be done in O(1), reducing the overall complexity from O(n²) to just O(n). Also, the whole thing can be done in a single line with a list comprehension:

>>> nums = [1, 2, 4, 5, 11]    
>>> target = 6    
>>> nums_set = set(nums)
>>> pairs = [(n, target-n) for n in nums_set if target-n in nums_set and n <= target/2]
>>> print(pairs)
[(1, 5), (2, 4)]

For printing the pairs or some message, you can use the or keyword. x or y is interpreted as x if x else y, so if the result set is empty, the message is printed, otherwise the result set itself.

>>> pairs = []    
>>> print(pairs or "No pairs found")
No pairs found

Update: The above can fail, if the number added to itself equals the target, but is only contained once in the set. In this case, you can use a collections.Counter instead of a set and check the multiplicity of that number first.

>>> nums = [1, 2, 4, 5, 11, 3]
>>> nums_set = set(nums)
>>> [(n, target-n) for n in nums_set if target-n in nums_set and n <= target/2]
[(1, 5), (2, 4), (3, 3)]
>>> nums_counts = collections.Counter(nums)
>>> [(n, target-n) for n in nums_counts if target-n in nums_counts and n <= target/2 and n != target-n or nums_counts[n] > 1]
[(1, 5), (2, 4)]
tobias_k
  • 81,265
  • 12
  • 120
  • 179
  • Thank you for this, it's very insightful. I'm currently trying to beef up my application to apply for a Masters course in CompSci (coming from engineering) and so doing these sorts of problems, and seeing compact solutions like yours, are a very useful knowledge tool! But if you don't mind, could you please explain the 3rd line of your update (/4th of your original) –  Jul 24 '17 at 18:55
0

List your constraints first!

  1. numbers added must be unique
  2. only 2 numbers can be added
  3. the length of the array can be arbitrary
  4. the number to be summed to can be arbitrary

& Don't skip preprocessing! Reduce your problem-space.

2 things off the bat:

Starting after your 2 print statements, the I would do array = list(set(array)) to reduce the problem-space to [7, 9, 13, 14, 23, 32, 41, 45].

Assuming that all the numbers in question will be positive, I would discard numbers above number. :
array = [x for x in array if x < number] giving [7, 9, 9, 13, 13, 14]

Combine the last 2 steps into a list comprehension and then use that as array:

smaller_array = [x for x in list(set(array)) if x < number]

which gives array == [7, 9, 13, 14]

After these two steps, you can do a bunch of stuff. I'm fully aware that I haven't answered your question, but from here you got this. ^this is the kind of stuff I'd assume google wants to see.

Rob Truxal
  • 5,856
  • 4
  • 22
  • 39