简体   繁体   中英

Generate random unique numbers in a list that sums to 0

Problem is that I need to generate a total of N numbers to be able to get the sum of 0. for example. if N=4 it should generate [1, 0, −3, 2] or [−2, 1, −4, 5].

Here's my code so far, it generates random int that sums to the value of N though, and it's random total number of values in the list.

from random import randint

def solution(N):
    values = []
    while 1 <= N <= 100:
         value = randint(1, N)
         values.append(value)
         N = N - value
    return values

print(solution(3))

You can use random.sample , then simply get the last value to be the negative value of the sum of the first three values:

import random
N=4
l = random.sample(range(-10, 10), k=3)
print(l + [-sum(l)])

For example it will output:

[-5, 1, -8, 12]

Which:

print(sum([-5, 1, -8, 12]))

Gives:

0

This doesn't completely ensure the final number is unique, but it is rather unlikely that it will already exist in the list.

def gen_unique_zero_sum_list(num_members):
    ret = []
    for i in range(int(num_members) - 1):
        candidate = random.randint(-100, 100)
        while candidate in ret:
            candidate = random.randint(-100, 100)
        ret.append(candidate)
    ret.append(-sum(ret))
    return ret

The fact that the above solution did not ensure uniqueness bugged me. Here's an enhanced version that should ensure uniqueness:

def gen_unique_zero_sum_list(num_members, min=-100, max=100):
    if int(num_members) < 1:
        raise ValueError
    ret = []
    # populate as many as we can randomly
    for i in range(0, int(num_members) - 2, 1):
        candidate = random.randint(min, max)
        while candidate in ret:
            candidate = random.randint(min, max)
        ret.append(candidate)
    if int(num_members) > 1:
        while len(ret) < int(num_members):
            # at this point we could get a forced duplicate
            candidate = random.randint(min, max)
            while candidate in ret:
                candidate = random.randint(min, max)
            final = -(sum(ret) + candidate)
            if final in ret or final == candidate:
                # we would have a duplicate, force two new numbers
                continue
            ret.append(candidate)
            ret.append(final)
    else:
        # this will always be zero, by definition
        ret.append(-sum(ret))
    return ret

An option in solving this problem is recursion. We can generate our first value of our list from the range from low to high, add it to the list, go back through the function but now with limited options (due to uniqueness) until we get to the point where there is only one spot left. At that point either we have succeeded in finding a list or we have failed and the previous levels of recursion will search for other values at earlier indices that may work. Another possibility is that there are no possible lists (eg low to high are both positive, options run out before last spot) in which case our function will return None.

import random


def foo(n, low, high):
    """Helper function for recursive function"""

    def rbar(arr):
        """Recursive function to create a 'random' list of n integers
        between low and high (exclusive) such that the sum of the list is 0
        and the entries of the list are unique."""

        # if one spot left check if -sum(arr) in arr
        if len(arr) == n-1:
            if -sum(arr) not in arr and -sum(arr) in range(low, high):
                    return arr + [-sum(arr)]

        # if more than one spot then generate the next possible values
        # and try to get to a full list of size n with those
        else:
            # loop through shuffled options (randomness here)
            options = [x for x in range(low, high) if x not in arr]

            for opt in random.sample(options, len(options)):
                # if recursively going through function produces a list then return it
                if rbar(arr + [opt]) is not None:
                    return rbar(arr + [opt])

            # if nothing worked then return None
            return


    if n==0:
        return []
    elif n==1 and 0 in range(low, high):
        return [0]
    elif n==1 and 0 not in range(low, high):
        return None
    else:
        return rbar([])


k = foo(4, -10, 10)

if k is not None:
    print("List: {}, Sum:{}".format(k, sum(k)))
else:
    print(None)

The simplest solution to this would be to include in the array 1, 2, 3 ... N-1 and the final element of the array is the sum of the previous elements multiplied by -1:

 function findSet(N) { var sum = 0; var r = []; if (N === 1) { r[0] = 0; } else { for (var i = 1; i < N; i++) { r[i - 1] = i; sum += i; } r[N - 1] = -sum; } return r; } function printArray(a) { console.log('[' + a.join(',') + ']'); } printArray(findSet(1)); printArray(findSet(2)); printArray(findSet(3)); printArray(findSet(4)); printArray(findSet(5));

They are not random, but at least unique ;) and the solution is fast.

def solution(N):
    l = random.sample(range(-10, 10), k=(N-1))
    return (l + [-sum(l)])

Try this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM