Разделить число на составляющие суммы
есть ли эффективный алгоритм для разделения числа на N
подразделы так, чтобы сумма чисел складывалась с исходным, с базовым минимумом? Например, если я хочу разделить 50 на 7 подразделов и иметь базовый минимум 2, я мог бы сделать 10,5,8,2,3,5,17
(а также любое другое количество комбинаций). Я хотел бы сохранить числа как целые числа и относительно случайные, но я не уверен, как эффективно генерировать числа, которые суммируются с оригиналом и не включают числа ниже чем данный минимум. Есть предложения?
EDIT-просто чтобы скопировать / вставить мой комментарий, целые числа не должны быть уникальными, но я хочу избежать равных размеров для всех из них (например, 50 разделить на 10 равных размеров) каждый раз.
9 ответов
вот алгоритм:
- разделить
N
bym
здесьN
ваш номер иm
количество подразделов. - округлить результат до ближайшего значения и присвоить это значение всем подразделам.
- добавьте по одному в каждый подраздел, пока значения не сложатся до
N
. В этот момент, еслиN
было 50 иm
было 7, Ты бы 8, 7, 7, 7, 7, 7, 7 - итерация от 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
разделы помощи по модулю и случайные.
это не будет работать, как ожидалось, если слишком много разделов для этого числа. (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/