Разделить число на составляющие суммы

есть ли эффективный алгоритм для разделения числа на N подразделы так, чтобы сумма чисел складывалась с исходным, с базовым минимумом? Например, если я хочу разделить 50 на 7 подразделов и иметь базовый минимум 2, я мог бы сделать 10,5,8,2,3,5,17 (а также любое другое количество комбинаций). Я хотел бы сохранить числа как целые числа и относительно случайные, но я не уверен, как эффективно генерировать числа, которые суммируются с оригиналом и не включают числа ниже чем данный минимум. Есть предложения?

EDIT-просто чтобы скопировать / вставить мой комментарий, целые числа не должны быть уникальными, но я хочу избежать равных размеров для всех из них (например, 50 разделить на 10 равных размеров) каждый раз.

9 ответов


вот алгоритм:

  1. разделить N by m здесь N ваш номер и m количество подразделов.
  2. округлить результат до ближайшего значения и присвоить это значение всем подразделам.
  3. добавьте по одному в каждый подраздел, пока значения не сложатся до N. В этот момент, если N было 50 и m было 7, Ты бы 8, 7, 7, 7, 7, 7, 7
  4. итерация от 0 до m-1, шагая мимо 2, и добавьте случайное число между -(currentValue-base) и currentValue-base. Добавьте обратное этому числу в соседнее ведро. Если у вас есть нечетное количество ведер, то на последнем ведре вместо добавления обратного этого числа к соседнему ведру добавьте его ко всем другим ведрам распределенным образом, подобным шагам 2 и 3 выше.

производительность: Шаг 1 O(1), шаги 2, 3 и 4 O(m), так что в целом это O(m).


вы можете легко удалить требование минимума, вычитая минимальное время N из числа, генерируя N подразделов и добавляя минимум. В вашем примере проблема сводится к разбиению 36 на 7 целых чисел, и вы дали разбиение 8,3,6,0,1,3,15.

остальная часть решения зависит от характера "относительно случайного" требования. Для некоторых минимальный случайности, попробуйте выбрать число последовательно между 0 и unsplitted части (например, между Сначала 0 и 36, получив 8, затем между 0 и 28, получив 3 и так далее 7 раз). Если этого недостаточно, вам нужно сначала определить случайность.


вот псевдослучайное решение [Обратите внимание, что решение может быть предвзятым, но будет относительно случайным].

input:
n - the number we should sum up to
k - the number of 'parts'
m - minimum

(1) split n into k numbers: x1,x2,...,xk such that x1+...+xk = n, and the numbers 
    are closest possible to each other [in other words, x1 = x2 = ... = n/k where 
    possible, the end might vary at atmost +-1.]
(2) for each number xi from i=1 to k-1:
       temp <- rand(m,xi)
       spread x - temp evenly among xi+1,...,xk
       xi <- temp
(3) shuffle the resulting list.

по части 1, например:n=50, k = 7, вы будете: x1=x2=...=x6=7,x7=8, нет проблем с вычислением и заполнением такого списка линейным временем.

производительность:

как сказано, Шаг 1-O (k).

Step2, с наивной реализацией O (k^2), но так как вы распределяете результат temp-xi равномерно, есть O(k) реализация, только с сохранением и изменением delta.

Step3 - это просто простое перемешивание, O (k)

Общая характеристика: O (k) С Delta ходе реализации Шаг2


Ну я придумал что-то "просто для удовольствия".

Он идет постепенно, начиная с minimum до number и заполняет массив с N разделы помощи по модулю и случайные.

см. jsFiddle здесь.

это не будет работать, как ожидалось, если слишком много разделов для этого числа. (ie number < N(N+1)/2)


вот пример java кода, создающего запрошенное перераспределение чисел. Это рекурсивный подход, мы разлагаем проблему на 2 подзадачи : если мы хотим разложить число на сумму компонентов среди n корзин, то мы пытаемся рассмотреть подсчет за раз, и для каждого из них делегируем обнаружение оставшегося разложения рекурсивному вызову для перераспределения среди (n-1) корзин. Запрашиваемый порог учитывается при обработке конкретного subnumber (в цикле for).

import java.util.ArrayList;
import java.util.List;

public class TestFigures {

    public static List<List<Integer>> computeRepartitionNumber(int number_to_decompose, int number_of_subnumbers, int threshold_number) {
        List<List<Integer>> resultRec = new ArrayList<>();

        if (number_of_subnumbers == 1) {
            List<List<Integer>> resultEnd = new ArrayList<>();
            ArrayList<Integer> unitary = new ArrayList<>();
            resultEnd.add(unitary);
            unitary.add(number_to_decompose);
            return resultEnd;
        }

        for (int i = threshold_number; i <= number_to_decompose-threshold_number; i++) {
            int remain = number_to_decompose - i;
            List<List<Integer>> partialRec = computeRepartitionNumber(remain, number_of_subnumbers - 1, threshold_number);
            for(List<Integer> subList : partialRec){
                subList.add(i);             
            }
            resultRec.addAll(partialRec);
        }
        return resultRec;

    }

    public static void main(String[] args) {
        List<List<Integer>> superlist = computeRepartitionNumber(5, 2, 1);
        System.out.println(superlist.size());
        System.out.println(superlist);

    }

}

import random

def split_given_number_into_n_random_numbers(number, number_of_subsections, min_random_number_desired = 0):
    cumulative_sum_of_random_numbers = 0
    current_subsection = 1
    max_random_number = int(number/number_of_subsections)
    if min_random_number_desired > max_random_number:
        print("ERROR: Cannot have min number as {} and split {} in {} subsections".format(min_random_number_desired,
                                                                                          number, number_of_subsections))
        return False

    while (True):
        random_number = random.randint(min_random_number_desired, max_random_number)
        print("Random number {} = {}".format(current_subsection, random_number))
        cumulative_sum_of_random_numbers += random_number
        # print("Cumulative sum {}".format(sum_of_num))
        number -= random_number
        current_subsection += 1
        if current_subsection == number_of_subsections:
            random_number = number
            print("Random number {} = {}".format(current_subsection, random_number))
            cumulative_sum_of_random_numbers += random_number
            break

    print("Final cumulative sum of random numbers = {}".format(cumulative_sum_of_random_numbers))
    return True

if __name__ == '__main__':
    split_given_number_into_n_random_numbers(50, 7, 2)

теперь, если вы хотите, чтобы минимальное число было чем-то еще, кроме 2, измените его на любое значение, предоставленное number_of_subsections * min_random_number_desired


Я знаю, что прошло много времени, но я хотел бы добавить свой ответ, чтобы помочь кому-то здесь мой код с помощью рекурсии

#include <stdio.h>
#include <stdlib.h>

void print(int n, int * a) {
int i ; 
for (i = 0; i <= n; i++) {
    printf("%d", a[i]); 
   i < n ? printf(" + ") : printf("");
}
printf("\n"); 
}

void integerPartition(int n, int * a, int level){
int first; 
int i; 
if (n < 1) return ;    
    a[level] = n;
print(level, a);
first = (level == 0) ? 1 : a[level-1];
for(i = first; i <= n / 2; i++){
    a[level] = i; 
    integerPartition(n - i, a, level + 1);
}
}
int main(int argc, char ** argv){
int n = 10;     
int * a = (int * ) malloc(sizeof(int) * n); 
integerPartition (n, a, 0); 
return(0);
}

здесь n равно 10, но u может сделать его похожим на запрос пользователя, объявить размер a с помощью нового оператора !


позвольте мне написать это в Python.

предположим, что у вас есть 50 элементов для разделения на 7 ящиков, и вы хотите, по крайней мере, два внутри каждого из них.

N_init = 50
s = 2
m = 7

мы помещаем S элементов по умолчанию в каждом поле, так что мы остаемся с N элементов.

N = N_init - s*m

мы рисуем m случайных чисел, сортируем их, добавляем N на спине. Это похоже на случайную вставку M закладок в книгу из N страниц. Количество страниц между последовательными закладками является случайным. (У нас было так, что мы уверены, что каждая коробка имеет минимум элементов s)

a = sorted([random.randint(0,N+1) for i in range(m)])
a.append(N)
result = [j-i+s for(i,j) in zip(a[0:m],a[1:m+1])]

готово!


Я работал над чем-то подобным, и вот что я придумал.

Вы можете сделать это O (N-1) используя некоторые вычисления на каждом шаге. Вы начинаете с выбора случайного числа между минимальным и максимальным числом для каждого места. Для каждого места максимальное число рассчитывается путем вычитания (Min_Number * Remaining_Spots)из остатка.

например: для первого места вы выбираете число между 2 и 38. Вы получаете это, вычитая (7-1)*2 из 50. например, 50 - 12 = 38.

Как только вы выберете число, скажем, 19, то для следующего места диапазон составляет 2-21. т. е. 50-19-(5*2) = 21..

..и так далее.

вот фрагмент кода:

function splitNumIntoXRandomComponents(num, x, min_num) {

var components = [];    

var count = 1;
var cumulative = 0;
var balance = num;

for (var i = 0; i<x-1; i++) {

    //max num for this spot
    var max_num = balance - ((x-count)*min_num);

    //to avoid big numbers in the beginning and min numbers at the end
    if (Math.random() > 0.5){ //0.5 can be tuned to your liking 
        max_num = Math.floor(max_num / 2) + min_num;
    }

    //generate the number for the spot at 'count'
    var c = Math.floor(Math.random()*(max_num-min_num+1)+min_num);

    //adjust balances
    cumulative += c;
    balance -= c;       
    count++;    

    //store this number
    components.push(c);                     

}

//push remaining balance into the last spot
components.push(balance);

//print numbers
console.log(components);

}

for (var i=0; i<10; i++) {
    splitNumIntoXRandomComponents(50, 7, 2);
}

вот пример вывода:

[34, 2, 4, 3, 3, 2, 2]
[14, 12, 8, 8, 4, 2, 2]
[7, 4, 26, 5, 2, 3, 3]
[8, 2, 16, 4, 4, 9, 7]
[20, 8, 4, 4, 7, 4, 3]
[3, 34, 4, 2, 2, 2, 3]
[10, 5, 15, 2, 7, 5, 6]
[6, 3, 10, 4, 10, 3, 14]
[31, 4, 2, 3, 5, 2, 3]
[7, 5, 2, 9, 9, 2, 16]

вот jsFiddle: http://jsfiddle.net/wj81kvsc/6/