-1

I am programming a knapsack encryption algorithm. I am new to Python programming. I have a list and an integer value I have determined. I want to find what elements in my list will sum up to my integer value. I can get it running for two elements or less but I can not run it for more than two elements.

Assume:

privkey = [2,3,6,13,27,52]
cipher = 9

My current function can run the scenario above:

searchList = []
for i, number in enumerate(privkey[:-1]):  
    complementary = cipher - number
    if complementary in privkey[i+1:]:  
        searchList.append(number)
        searchList.append(complementary)
        print("Solution Found: {} and {}".format(number, complementary))
        break
else:  
    print("No solutions exist")
print(searchList)

The expected output of this will be [3,6] and indeed works.

However if cipher is changed to a condition which requires the sum of more than three characters for example:

cipher = 11

This would require the sum of privkey[0]+privkey[1]+privkey[2].

How can I create a code that covers these bases as well?

4 Answers4

1

You can use this code (For python3). This code will provide you all possible combinations from privkey list which satisfy the cipher integer.

from itertools import combinations

privkey = [2,3,6,13,27,52]
cipher = 9
character = 2  # change it to get sum of dynamic no. of character

possible_combination = list(combinations(privkey, character))

li =[each for each in possible_combination if sum(each) == cipher]
if li:
    print("List-> ", *li)
else:
    print("No combination found from privkey list to satisfy the cipher integer.")

OUTPUT:

(3, 6)
Shivam Gupta
  • 201
  • 1
  • 5
  • Why don't you edit it to put `character` in a loop for all the possible no. of characters, and `break` when you get a match? – Aryerez Nov 27 '19 at 06:31
  • This code will get all possible combinations from privkey list instead of only one. If we want to find only first match, we can use loop (and break the loop on first match) instead of list comprehension. – Shivam Gupta Nov 27 '19 at 06:33
  • There may be other issues here, but don’t call `list()` on the result of `itertools.combinations()`. – AMC Nov 27 '19 at 06:34
  • I meant that now, if he wants to check for the combination of 3 or 4 numbers instead of 2 (like in the question), he needs to change that manually and re-run it. And it would be better to use a loop, that automatically check all the options for him. – Aryerez Nov 27 '19 at 06:36
  • @Aryerez I write a code if only single character value is given. But in your case, you are correct. You must use loop to check combination from 1 to list length. – Shivam Gupta Nov 27 '19 at 06:42
0

It needs to be done recursively. This is the function I built, it may not be optimized:

def find_sum(my_list, left_list, target):
    if sum(my_list) == target:
        return my_list

    # Add this condition if all the numbers are positive (e.g once you are over the target, don't continue to try numbers
    if sum(my_list) > target:
        return

    if len(left_list) == 0:
        return
    for i in range(len(left_list)):
        current_number = left_list.pop(0)
        my_list.append(current_number)
        result = find_sum(my_list, left_list, target)
        if result:
            return result
        my_list.pop()
        left_list.append(current_number)
    return

privkey = [2,3,6,13,27,52]
cipher = 11
print(find_sum([], privkey, cipher))

Output:

[2, 3, 6]

Or a solution based on @Shivam Gupta's method, but that checks for all the possible number of items possible:

from itertools import combinations
privkey = [2,3,6,13,27,52]
cipher = 11
for character in range(1, len(privkey) + 1):
    possible_combination = list(combinations(privkey, character))
    li =[each for each in possible_combination if sum(each) == cipher]
    if li:
        print("List-> ", *li)
        break
else: # Will happen only if the loop ended with no match
    print("No combination found from privkey list to satisfy the cipher integer.")

Output:

List->  (2, 3, 6)
Aryerez
  • 3,417
  • 2
  • 9
  • 17
  • what is the variable "left_list" set to? – socket_programmer Nov 27 '19 at 06:36
  • `left_list` is the list with the numbers that are *left* to check (haven't been used yet), and `my_list` are the numbers already included in the current check. – Aryerez Nov 27 '19 at 06:40
  • I want to use the code [int(x in li) for x in privkey] to convert the list to a binary representation. Currently the values are sharing the same element in the list, in this case li[0]. How would I get them to each populate their own separate position in the list? – socket_programmer Nov 27 '19 at 07:29
0

You are on the right track with what you have. The problem is with complementary. If you wanted to do this approach you would need to implement a recursive solution in order to continuously decrement that value.

This solution is much easier to read. I'm not sure if it is the fastest solution but it is easy to understand. Basically instead of going through each list item 1 by 1 and trying to see if anything matches. Just get a list of all the possible combinations and filter out the ones that do not add up to expected result.

from itertools import combinations

privkey = [2,3,6,13,27,52]
cipher = 9

# Get All Possible Combinations Of Numbers In List
combinations = sum([list(
    map(list, combinations(privkey, i))) 
        for i in range(len(privkey) + 1)], [])

# Check If Any Combinations Sum To Cipher
solutions = [x for x in combinations if sum(x) == cipher)]

# Print Solutions
for i in solutions:
    print("Solution Found: {0}".format(i)
Noctrim
  • 16
  • 5
0

It seems you need to find the sub-array sum. A simple solution is to consider all subarrays one by one and check the sum of every subarray. If you find interesting then Refer Here: Find the subarray with given sum

Hope it will helps : )

VJAYSLN
  • 473
  • 4
  • 12