Разбиение числа на несколько почти равных частей

Я хотел бы разделить число на почти равное количество значений в каждом разделе. Единственным критерием является то, что каждый раздел должен быть между 60 и 80.

Например, если у меня есть value = 300, это означает, что 75 * 4 = 300.

Я хотел бы знать, как получить эти 4 и 75 в приведенном выше примере. В некоторых случаях все разделы не обязательно должны иметь одинаковую ценность, но они должны находиться между 60 and 80. Можно использовать любые ограничения (сложение, вычитание и т.д.). Однако выходные данные не должны быть с плавающей запятой.

Также дело не в том, что общая сумма должна быть ровно 300, как в этом случае, но они могут составлять максимум +40 от общей суммы, и, таким образом, в случае 300 числа могут в сумме составлять 340, если это необходимо.


person user8162    schedule 13.11.2014    source источник
comment
Что делать, если ваш номер не делится ни на одно число от 60 до 80? скажем, это 90 или 161 или простое число, что тогда должно быть на выходе?   -  person Rashid    schedule 13.11.2014
comment
Они также могут быть представлены как 79+75+70+76 = 300. Единственное условие состоит в том, что эти числа должны быть между 60 и 80. Также они должны быть оптимальными (в основном между 65 и 75).   -  person user8162    schedule 13.11.2014
comment
Хорошо, я думал, что разрешено только умножение!   -  person Rashid    schedule 13.11.2014
comment
Что еще разрешено? Вычитание, деление, степень?   -  person Divakar    schedule 13.11.2014
comment
также допускается вычитание и деление.   -  person user8162    schedule 13.11.2014
comment
Если вы разрешаете только сложение/вычитание, вы можете легко решить это с помощью линейного программирования. Добавление большего количества операций сделает эту проблему слишком неограниченной и, по сути, сделает вашу проблему решаемой только методом грубой силы. Укажите, какие операции и другие ограничения необходимы для решения этой задачи. По сути, это более общий случай проблемы суммы подмножества.   -  person rayryeng    schedule 13.11.2014
comment
Можно использовать любые ограничения. Очевидно, это будет сделано в Matlab.   -  person user8162    schedule 13.11.2014
comment
Конечно, это будет сделано в MATLAB, но сама задача слишком неограниченна. Я гарантирую вам, что эта проблема является NP-Hard. Пожалуйста, отредактируйте описание проблемы, чтобы сделать его более понятным, например, пояснение, которое вы сделали к нашим комментариям в своем сообщении.   -  person rayryeng    schedule 13.11.2014
comment
ХОРОШО. Предполагая только дополнение, я собираюсь написать ответ. Пожалуйста, подождите несколько минут. Кстати, вы можете генерировать результаты с плавающей запятой? Например, что если размер раздела 70,9 или 72,5?   -  person rayryeng    schedule 13.11.2014
comment
Вопрос отредактирован...   -  person user8162    schedule 13.11.2014


Ответы (1)


Предполагая только сложение, вы можете сформулировать эту задачу в виде задачи линейного программирования. Вы должны выбрать целевую функцию, которая максимизирует сумму всех факторов, выбранных для получения этого числа для вас. Следовательно, ваша целевая функция будет:


(источник: codecogs.com)
.

В этом случае n будет числом факторов, которые вы используете, чтобы попытаться разложить свое число на составляющие. Каждый x_i является особым фактором в общей сумме значения, которое вы хотите разложить. Я также собираюсь предположить, что ни один из факторов не может быть с плавающей запятой, а может быть только целым числом. Таким образом, вам нужно использовать особый случай линейного программирования, называемый целочисленным программированием, где ограничения и фактическое решение вашей проблемы все в целых числах. В общем случае задача целочисленного программирования формулируется так:

На самом деле вы пытаетесь минимизировать эту целевую функцию, чтобы получить вектор параметров x, на который распространяются все эти ограничения. В нашем случае x будет вектором чисел, где каждый элемент составляет часть суммы значения, которое вы пытаетесь разложить (300 в вашем случае).

У вас есть неравенства, равенства, а также границы x, которые должен соблюдать каждый параметр в вашем решении. Вам также необходимо убедиться, что каждый параметр x является целым числом. Таким образом, в MATLAB есть функция с именем intlinprog, которая будет выполнять это для ты. Однако эта функция предполагает, что вы минимизируете целевую функцию, поэтому, если вы хотите максимизировать, просто минимизируйте отрицательное значение. f – это вектор весов, который будет применяться к каждому значению в вашем векторе параметров, и с нашей целевой функцией вам просто нужно установить все эти значения в -1.

Поэтому, чтобы сформулировать свою проблему в рамках целочисленного программирования, вы фактически делаете:


(источник: codecogs.com)

V будет значением, которое вы пытаетесь разложить (например, 300).


Стандартный способ вызова intlinprog заключается в следующем:

x = intlinprog(f,intcon,A,b,Aeq,beq,lb,ub);

f — это вектор, который взвешивает каждый параметр решения, которое вы хотите решить, intcon обозначает, какие из ваших параметров должны быть целыми числами. В этом случае вы хотите, чтобы все они были целыми числами, поэтому вам нужно будет предоставить возрастающий вектор от 1 до n, где n — это количество факторов, на которые вы хотите разложить число V (так же, как и раньше). A и b — это матрицы и векторы, которые определяют ваши ограничения неравенства. Поскольку вы хотите равенства, вы должны установить это пустым ([]). Aeq и beq такие же, как A и b, но для равенства. Поскольку здесь у вас есть только одно ограничение, вы просто создадите матрицу из 1 строки, где каждое значение равно 1. beq будет единственным значением, обозначающим число, которое вы пытаетесь разложить на множители. lb и ub — это нижняя и верхняя границы для каждого значения в наборе параметров, с которым вы ограничиваетесь, поэтому это будет 60 и 80 соответственно, и вам нужно будет указать вектор, чтобы гарантировать, что каждое значение параметров ограничено между этими двумя диапазонами.

Теперь, поскольку вы не знаете, сколько факторов будет равномерно разлагать ваше значение, вам придется зацикливаться на заданном наборе факторов (например, от 1 до 10, или от 1 до 20 и т. д.), поместите свои результаты в массив cell, затем вам придется вручную проверить себя, было ли успешно целочисленное разложение.

num_factors = 20; %// Number of factors to try and decompose your value
V = 300;
results = cell(1, num_factors);

%// Try to solve the problem for a number of different factors
for n = 1 : num_factors
    x = intlinprog(-ones(n,1),1:n,[],[],ones(1,n),V,60*ones(n,1),80*ones(n,1));
    results{n} = x;
end

Затем вы можете просмотреть results и посмотреть, какое значение n помогло разложить ваше число на указанное количество факторов.

Одна небольшая проблема заключается в том, что мы также не знаем, сколько факторов мы должны проверить. К сожалению, у меня нет ответа, поэтому вам придется играть с этим значением, пока вы не получите хорошие результаты. Это также неограниченный параметр, и я расскажу об этом позже в этом посте.


Однако intlinprog был выпущен только в последних версиях MATLAB. Если вы хотите сделать то же самое без него, вы можете использовать linprog, которая представляет собой версию целочисленного программирования с плавающей запятой... на самом деле, это просто основная структура линейного программирования. Вы бы назвали linprog таким образом:

x = linprog(f,A,b,Aeq,beq,lb,ub);

Все переменные одинаковы, за исключением того, что intcon здесь не используется... что имеет смысл, поскольку linprog может генерировать числа с плавающей запятой как часть своего решения. Из-за того, что linprog может генерировать решения с плавающей запятой, вы можете сделать это, если хотите убедиться, что для заданного значения n вы можете перебрать свои результаты, взять пол результата и вычесть с окончательным результатом, и просуммировать результат. Если вы получаете значение 0, это означает, что вы получили полностью целочисленный результат. Поэтому вам нужно будет сделать что-то вроде:

num_factors = 20; %// Number of factors to try and decompose your value
V = 300;
results = cell(1, num_factors);

%// Try to solve the problem for a number of different factors
for n = 1 : num_factors
    x = linprog(-ones(n,1),[],[],ones(1,n),V,60*ones(n,1),80*ones(n,1));
    results{n} = x;
end

%// Loop through and determine which decompositions were successful integer ones
out = cellfun(@(x) sum(abs(floor(x) - x)), results);

%// Determine which values of n were successful in the integer composition.
final_factors = find(~out);

final_factors будет содержать указанное вами количество факторов, которые были успешными в целочисленной декомпозиции. Теперь, если final_factors пусто, это означает, что не удалось найти ничего, что могло бы разложить значение на целочисленные множители. Отметив описание вашей проблемы, вы сказали, что можете допускать допуски, поэтому, возможно, просмотрите results и определите, какая общая сумма лучше всего соответствует значению, а затем выберите любое количество факторов, которые дали вам этот результат, в качестве окончательного ответа.


Теперь, обращая внимание на мои комментарии, вы увидите, что эта проблема очень не ограничена. Вы не знаете, сколько факторов требуется для получения целочисленной декомпозиции вашего значения, поэтому нам пришлось использовать полугрубую силу. Фактически, это более общий случай проблемы суммы подмножества. Эта проблема является NP-complete. По сути, это означает, что неизвестно, существует ли алгоритм с полиномиальным временем, который можно использовать для решения такого рода задач, и что единственный способ получить правильное решение — перебрать каждое возможное решение и проверить, работает с указанной проблемой. Обычно перебор решений требует экспоненциального времени, что очень сложно для больших задач. Еще один интересный факт заключается в том, что современные криптографические алгоритмы используют NP-полную неразрешимость как часть своего зашифрованного текста и шифрования. По сути, они делают ставку на тот факт, что единственный способ определить правильный ключ, который использовался для шифрования обычного текста, — это проверить все возможные ключи, что является неразрешимой проблемой... особенно если вы используете 128-битный код. шифрование! Это означает, что вам придется проверить 2^128 возможности, и, если предположить, что компьютер умеренно быстрый, в худшем случае время на поиск правильного ключа займет больше, чем текущий возраст Вселенной. Прочтите этот классный пост в Википедии для получения более подробной информации о неподатливости в отношении взлома ключей в криптографии. .

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

В Математическом институте Клэя есть так называемые задачи тысячелетия. сайт, вы получите миллион долларов.

Кроме того, это для каждой проблемы, так что one problem solved == 1 million dollars!


(источник: quickmeme.com )

Задача NP входит в число семи проблем, требующих решения. Если я правильно помню, пока решена только одна проблема, и эти проблемы были впервые обнародованы в 2000 году (отсюда тысячелетие...). Итак... прошло около 14 лет, а решена только одна проблема. Пусть это вас не обескураживает! Если вы хотите потратить некоторое время и попытаться решить одну из проблем, сделайте это!


Надеюсь, этого будет достаточно, чтобы вы начали. Удачи!

person rayryeng    schedule 13.11.2014
comment
@Divakar - Спасибо :) Оптимизация и численные методы - еще одна моя любовь. Если это не обработка изображений, то уж точно это. - person rayryeng; 13.11.2014
comment
Это действительно мило, продолжайте в том же духе! - person Divakar; 13.11.2014
comment
+1 Этого определенно должно хватить, чтобы начать оригинальный постер :-) - person Luis Mendo; 13.11.2014
comment
@Glorfindel, ты постоянно просматриваешь мои сообщения и восстанавливаешь неработающие ссылки. Большое спасибо за ваши усилия и поддержание высокого качества моих ответов. - person rayryeng; 23.09.2019
comment
Пожалуйста; спасибо, что написали их в первую очередь. Возможно, вы уже заметили, что это автоматизированный скрипт (подробности см. в сводке по редактированию), почти вся работа была написана. - person Glorfindel; 23.09.2019
comment
@Glorfindel Это с открытым исходным кодом? Я хотел бы увидеть, как это выглядит! - person rayryeng; 23.09.2019