Поиск всех комбинаций нескольких переменных, суммирующих до 1
Я пытаюсь решить уравнение
x1 + x2 + x3 + .... + xn = 1
где значения всех xi
ограничены [0, 0.1, 0.2, ..., 0.9, 1]
,
В настоящее время я решаю проблему, сначала генерируя n-мерный массив mat
где в каждом местоположении элемента значение является суммой значений осей, которые изменяются в axisValues = 0:0.1:1
:
mat(i,j,k,...,q) = axisValues(i) + axisValues(j) + ... + axisValues(q).
Затем я ищу все записи в результирующем массиве, которые равны единице. Код (показанный ниже для дальнейшего пояснения) работает нормально и был протестирован для 5 измерений. Проблема в том, что время выполнения увеличивается в геометрической прогрессии, и мне нужно, чтобы скрипт работал для нескольких измерений.
clear all
dim = 2; % The dimension of the matrix is defined here. The script has been tested for dim ≤ 5
fractiles(:,1) = [0:0.1:1]; % Produces a vector containing the initial axis elements, which will be used to calculate the matrix elements
fractiles = repmat(fractiles,1,dim); % multiplies the vector to supply dim rows with the axis elements 0:0.1:1. These elements will be changed later, but the symmetry will remain the same.
dim_len = repmat(size(fractiles,1),1,size(fractiles,2)); % Here, the length of the dimensions is checked, which his needed to initialize the matrix mat, which will be filled with the axis element sums
mat = zeros(dim_len); % Here the matrix mat is initialized
Sub=cell(1,dim);
mat_size = size(mat);
% The following for loop fills each matrix elements of the dim dimensional matrix mat with the sum of the corresponding dim axis elements.
for ind=1:numel(mat)
[Sub{:}]=ind2sub(mat_size,ind);
SubMatrix=cell2mat(Sub);
sum_indices = 0;
for j = 1:dim
sum_indices = sum_indices+fractiles(SubMatrix(j),j);
end
mat(ind) = sum_indices;
end
Ind_ones = find(mat==1); % Finally, the matrix elements equal to one are found.
Мне кажется, что следующая идея, использующая симметрию задачи, может помочь значительно сократить время вычислений:
Для двумерной матрицы все записи, которые удовлетворяют условию выше, лежат на диагонали от mat(1,11)
в mat(11,1)
от максимального значения x1
до максимального значения x2
,
Для трехмерной матрицы все записи удовлетворяют условию, лежащему на диагональной плоскости через mat(1,1,11)
, mat(1,11,1)
, mat(11,1,1)
от максимального значения x1
а также x2
до максимального значения x3
,
То же самое верно для более высоких измерений: все интересующие матричные элементы лежат на n-1
размерная гиперплоскость зафиксирована на наибольшем значении оси в каждом измерении.
Вопрос: есть ли способ напрямую определить индексы элементов на этих n-1
размерная гиперплоскость? Если это так, вся проблема может быть решена за один шаг без необходимости вычисления всех элементов n-мерной матрицы и последующего поиска элементов, представляющих интерес.
1 ответ
Математика:
Вместо того, чтобы идти по гиперкубу, мы решаем уравнение
x(1) + x(2) + ... + x(n) = 1
где каждый x(i)
может варьироваться в [0, 1/k, 2/k, ... (k-1)/k, 1]
вместо. В твоем случае k
будет 10, так как это приведет к процентам [0, 10, 20, ... 90, 100]
, Умножается на k
это соответствует диофантову уравнению
x(1) + x(2) + ... + x(n) = k,
где все x(i)
варьируются в [0, 1, 2, ... k-1, k]
,
Мы можем построить биекцию между этим и комбинаторной концепцией комбинаций с повторением.
Статья в Википедии даже косвенно упоминает основную биекцию утверждением:
Число мультиподмножеств размера k является числом неотрицательных целочисленных решений диофантова уравнения.
x1 + x2 + ... + xn = k
,
Для меньшего примера, скажем, мы идем с k=3
и проценты в [0, 33, 66, 100]
вместо. Учитывая все k-мультикомбинации множества {1,2,3}
:
RepCombs =
1 1 1
1 1 2
1 1 3
1 2 2
1 2 3
1 3 3
2 2 2
2 2 3
2 3 3
3 3 3
Затем мы сопоставляем их с вашими процентами, используя следующее правило: Для каждой строки i
если запись j
, затем добавьте 1/3
процента к соответствующей записи матрицы M(i,j)
, Первый ряд будет соответствовать [1/3 + 1/3 + 1/3, 0, 0] = [1,0,0]
, Общая матрица, сгенерированная этим процессом, будет выглядеть так:
M =
1.0000 0 0
0.6667 0.3333 0
0.6667 0 0.3333
0.3333 0.6667 0
0.3333 0.3333 0.3333
0.3333 0 0.6667
0 1.0000 0
0 0.6667 0.3333
0 0.3333 0.6667
0 0 1.0000
Код:
А теперь для кода MATLAB, который генерирует все это: я использую функцию nmultichoosek
из этого ответа и accumarray
для достижения нашей цели:
function M = possibleMixturesOfNSubstances(N, percentageSteps)
RepCombs = nmultichoosek(1:N, percentageSteps);
numCombs = size(RepCombs,1);
M = accumarray([repmat((1:numCombs).', percentageSteps, 1), RepCombs(:)], 1/percentageSteps, [numCombs, N]);
Если вы хотите, чтобы проценты в [0, 10, ... 90, 100]
и имеют 4 вещества, вызовите эту функцию, используя possibleMixturesOfNSubstances(4,10)