Все возможные перестановки десятичных чисел (сотых), которые в сумме дают 1 для заданной длины

Рассмотрим вектор s следующим образом:

s=seq(0.01, 0.99, 0.01)

> s
 [1] 0.01 0.02 0.03 0.04 0.05 0.06 0.07 
0.08 0.09 .......... 0.89 0.90 0.91 0.92 0.93 0.94 0.95 0.96 0.97 0.98 0.99

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

Например, если m=4 (т.е. количество столбцов), желаемая матрица будет примерно такой:

0.01 0.01 0.01 0.97
0.02 0.01 0.01 0.96
0.03 0.01 0.01 0.95
0.04 0.01 0.01 0.94
0.05 0.01 0.01 0.93
0.06 0.01 0.01 0.92
.
.
.
0.53 0.12 0.30 0.05
.
.
.
0.96 0.02 0.01 0.01
0.97 0.01 0.01 0.01
.
.
.
0.01 0.97 0.01 0.01
.
.
.

person 989    schedule 07.06.2016    source источник
comment
stackoverflow.com/questions/4632322/   -  person C8H10N4O2    schedule 07.06.2016
comment
Прежде чем опубликовать свой вопрос, я рассмотрел этот вопрос. Дело в том, что в R его нет. Более того, мой вопрос отличается от ссылки тем, что нужно учитывать заданную длину.   -  person 989    schedule 07.06.2016
comment
@ m0h3n, вы ищете combinations или permutations. Судя по всему, кажется, что вы ищете «перестановки». См. это.   -  person Joseph Wood    schedule 07.06.2016


Ответы (3)


Вот как это сделать с помощью рекурсии:

permsum <- function(s,m) if (m==1L) matrix(s) else do.call(rbind,lapply(seq_len(s-m+1L),function(x) unname(cbind(x,permsum(s-x,m-1L)))));
res <- permsum(100L,4L);
head(res);
##      [,1] [,2] [,3] [,4]
## [1,]    1    1    1   97
## [2,]    1    1    2   96
## [3,]    1    1    3   95
## [4,]    1    1    4   94
## [5,]    1    1    5   93
## [6,]    1    1    6   92
tail(res);
##           [,1] [,2] [,3] [,4]
## [156844,]   95    2    2    1
## [156845,]   95    3    1    1
## [156846,]   96    1    1    2
## [156847,]   96    1    2    1
## [156848,]   96    2    1    1
## [156849,]   97    1    1    1

Вы можете разделить на 100, чтобы получить дроби, а не целые числа:

head(res)/100;
##      [,1] [,2] [,3] [,4]
## [1,] 0.01 0.01 0.01 0.97
## [2,] 0.01 0.01 0.02 0.96
## [3,] 0.01 0.01 0.03 0.95
## [4,] 0.01 0.01 0.04 0.94
## [5,] 0.01 0.01 0.05 0.93
## [6,] 0.01 0.01 0.06 0.92

Объяснение

Сначала давайте определим входы:

  • s Это целевое значение, к которому должна суммироваться каждая строка в выходной матрице.
  • m Это количество столбцов, которые необходимо создать в выходной матрице.

Более эффективно и надежно вычислять результат, используя целочисленную арифметику, а не арифметику с плавающей запятой, поэтому я разработал свое решение для работы только с целыми числами. Следовательно, s является скалярным целым числом, представляющим целевую целочисленную сумму.


Теперь давайте рассмотрим последовательность, сгенерированную seq_len() для небазового случая:

seq_len(s-m+1L)

Это генерирует последовательность от 1 до максимально возможного значения, которое может быть частью суммы до s с оставшимися m столбцами. Например, подумайте о случае s=100,m=4: максимальное число, которое мы можем использовать, — это 97, участвующее в сумме 97+1+1+1. Каждый оставшийся столбец уменьшает максимально возможное значение на 1, поэтому мы должны вычесть m из s при вычислении длины последовательности.

Каждый элемент сгенерированной последовательности следует рассматривать как один из возможных «выборов» слагаемого при суммировании.


do.call(rbind,lapply(seq_len(s-m+1L),function(x) ...))

Затем для каждого выбора мы должны выполнить рекурсию. Мы можем использовать lapply() для этого.

Чтобы перейти вперед, лямбда сделает один рекурсивный вызов permsum(), а затем cbind() вернет значение с текущим выбором. Это создаст матрицу всегда одинаковой ширины для этого уровня рекурсии. Следовательно, вызов lapply() вернет список матриц одинаковой ширины. Затем мы должны связать их вместе, поэтому здесь мы должны использовать трюк do.call(rbind,...).


unname(cbind(x,permsum(s-x,m-1L)))

Тело лямбды довольно простое; мы cbind() текущий выбор x с возвращаемым значением рекурсивного вызова, завершая суммирование для этой подматрицы. К сожалению, мы должны вызывать unname(), иначе каждый столбец, который в конечном итоге будет установлен из аргумента x, будет иметь имя столбца x.

Самая важная деталь здесь — выбор аргументов для рекурсивного вызова. Во-первых, поскольку лямбда-аргумент x был только что выбран во время текущей рекурсивной оценки, мы должны вычесть его из s, чтобы получить новую цель суммирования, за достижение которой будет отвечать предстоящий рекурсивный вызов. Следовательно, первый аргумент становится s-x. Во-вторых, поскольку выбор x занимает один столбец, мы должны вычесть 1 из m, так что рекурсивный вызов будет иметь на один столбец меньше выходной матрицы.


if (m==1L) matrix(s) else ...

Наконец, давайте рассмотрим базовый случай. При каждом вычислении рекурсивной функции мы должны проверять, достигло ли m 1, и в этом случае мы можем просто вернуть требуемую сумму s.


Расхождение с плавающей запятой

Я изучил несоответствие между моими результатами и результатами psidom. Например:

library(data.table);

bgoldst <- function(s,m) permsum(s,m)/s;
psidom <- function(ss,m) { raw <- do.call(data.table::CJ,rep(list(ss),m)); raw[rowSums(raw)==1,]; };

## helper function to sort a matrix by columns
smp <- function(m) m[do.call(order,as.data.frame(m)),];

s <- 100L; m <- 3L; ss <- seq_len(s-1L)/s;
x <- smp(bgoldst(s,m));
y <- smp(unname(as.matrix(psidom(ss,m))));
nrow(x);
## [1] 4851
nrow(y);
## [1] 4809

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

x==do.call(rbind,c(list(y),rep(list(NA),nrow(x)-nrow(y))));
##          [,1]  [,2]  [,3]
##    [1,]  TRUE  TRUE  TRUE
##    [2,]  TRUE  TRUE  TRUE
##    [3,]  TRUE  TRUE  TRUE
##    [4,]  TRUE  TRUE  TRUE
##    [5,]  TRUE  TRUE  TRUE
##
## ... snip ...
##
##   [24,]  TRUE  TRUE  TRUE
##   [25,]  TRUE  TRUE  TRUE
##   [26,]  TRUE  TRUE  TRUE
##   [27,]  TRUE  TRUE  TRUE
##   [28,]  TRUE  TRUE  TRUE
##   [29,]  TRUE FALSE FALSE
##   [30,]  TRUE FALSE FALSE
##   [31,]  TRUE FALSE FALSE
##   [32,]  TRUE FALSE FALSE
##   [33,]  TRUE FALSE FALSE
##
## ... snip ...

Итак, в строке 29 у нас есть первое несоответствие. Вот окно вокруг этой строки в каждой матрице перестановок:

win <- 27:31;
x[win,]; y[win,];
##      [,1] [,2] [,3]
## [1,] 0.01 0.27 0.72
## [2,] 0.01 0.28 0.71
## [3,] 0.01 0.29 0.70 (missing from y)
## [4,] 0.01 0.30 0.69 (missing from y)
## [5,] 0.01 0.31 0.68
##      [,1] [,2] [,3]
## [1,] 0.01 0.27 0.72
## [2,] 0.01 0.28 0.71
## [3,] 0.01 0.31 0.68
## [4,] 0.01 0.32 0.67
## [5,] 0.01 0.33 0.66

Интересно, что отсутствующие перестановки обычно дают в сумме ровно 1, когда вы вычисляете сумму вручную. Сначала я подумал, что это функция CJ() data.table делает что-то странное с числами с плавающей запятой, но дальнейшее тестирование, похоже, указывает на то, что это делает rowSums():

0.01+0.29+0.70==1;
## [1] TRUE
ss[1L]+ss[29L]+ss[70L]==1;
## [1] TRUE
rowSums(CJ(0.01,0.29,0.70))==1; ## looks like CJ()'s fault, but wait...
## [1] FALSE
cj <- CJ(0.01,0.29,0.70);
cj$V1+cj$V2+cj$V3==1; ## not CJ()'s fault
## [1] TRUE
rowSums(matrix(c(0.01,0.29,0.70),1L,byrow=T))==1; ## rowSums()'s fault
## [1] FALSE

Мы можем обойти эту rowSums() причуду, применив ручной (и несколько произвольный) допуск в сравнении с плавающей запятой. Для этого нам нужно взять абсолютную разницу, а затем выполнить сравнение меньше чем с допуском:

abs(rowSums(CJ(0.01,0.29,0.70))-1)<1e-10;
## [1] TRUE

Следовательно:

psidom2 <- function(ss,m) { raw <- do.call(data.table::CJ,rep(list(ss),m)); raw[abs(rowSums(raw)-1)<1e-10,]; };
y <- smp(unname(as.matrix(psidom2(ss,m))));
nrow(y);
## [1] 4851
identical(x,y);
## [1] TRUE

Комбинации

Спасибо Джозефу Вуду за указание на то, что это действительно перестановки. Первоначально я назвал свою функцию combsum(), но переименовал ее в permsum(), чтобы отразить это откровение. И, как предположил Джозеф, можно модифицировать алгоритм для создания комбинаций, что можно сделать следующим образом, теперь соответствуя имени combsum():

combsum <- function(s,m,l=s) if (m==1L) matrix(s) else do.call(rbind,lapply(seq((s+m-1L)%/%m,min(l,s-m+1L)),function(x) unname(cbind(x,combsum(s-x,m-1L,x)))));
res <- combsum(100L,4L);
head(res);
##      [,1] [,2] [,3] [,4]
## [1,]   25   25   25   25
## [2,]   26   25   25   24
## [3,]   26   26   24   24
## [4,]   26   26   25   23
## [5,]   26   26   26   22
## [6,]   27   25   24   24
tail(res);
##         [,1] [,2] [,3] [,4]
## [7148,]   94    3    2    1
## [7149,]   94    4    1    1
## [7150,]   95    2    2    1
## [7151,]   95    3    1    1
## [7152,]   96    2    1    1
## [7153,]   97    1    1    1

Для этого потребовалось 3 изменения.

Во-первых, я добавил новый параметр l, что означает «лимит». По сути, чтобы гарантировать, что каждая рекурсия генерирует уникальную комбинацию, я заставляю каждый выбор быть меньше или равным любому предыдущему выбору в текущей комбинации. Это потребовало принятия текущего верхнего предела в качестве параметра l. В вызове верхнего уровня l можно просто установить по умолчанию на s, что в любом случае слишком велико для случаев, когда m>1, но это не проблема, поскольку это всего лишь один из двух верхних пределов, которые будут применяться во время генерации последовательности.

Вторым изменением, конечно же, было передать последний выбор x в качестве аргумента l при выполнении рекурсивного вызова в лямбда-выражении lapply().

Последнее изменение самое сложное. Последовательность выбора теперь должна быть вычислена следующим образом:

seq((s+m-1L)%/%m,min(l,s-m+1L))

Нижний предел должен был быть повышен с 1, используемого в permsum(), до самого низкого возможного выбора, который все еще допускал бы комбинацию убывающей величины. Наименьший возможный выбор, конечно, зависит от того, сколько столбцов еще предстоит создать; чем больше столбцов, тем больше «места» мы должны оставить для будущих выборов. Формула состоит в том, чтобы целочисленное деление s на m, но мы также должны эффективно «округлить», поэтому я добавляю m-1L перед делением. Я также рассматривал возможность деления с плавающей запятой, а затем вызова as.integer(ceiling(...)), но я думаю, что полностью целочисленный подход намного лучше.

Например, рассмотрим случай s=10,m=3. Чтобы получить сумму 10 с оставшимися 3 столбцами, мы не можем сделать выборку меньше 4, потому что тогда у нас не будет достаточного количества, чтобы произвести 10 без восхождения по комбинации. В этом случае формула делит 12 на 3, чтобы получить 4.

Верхний предел можно вычислить по той же формуле, что и в permsum(), за исключением того, что мы должны также применить текущий предел l, используя вызов min().


Я убедился, что моя новая функция combsum() ведет себя идентично функции IntegerPartitionsOfLength() Джозефа для многих случайных тестовых случаев со следующим кодом:

## helper function to sort a matrix within each row and then by columns
smc <- function(m) smp(t(apply(m,1L,sort)));

## test loop
for (i in seq_len(1000L)) {
    repeat {
        s <- sample(1:100,1L);
        m <- sample(2:5,1L);
        if (s>=m) break;
    };
    x <- combsum(s,m);
    y <- IntegerPartitionsOfLength(s,m);
    cat(paste0(s,',',m,'\n'));
    if (!identical(smc(x),smc(y))) stop('bad.');
};

Бенчмаркинг

Общий автономный тестовый код:

library(microbenchmark);
library(data.table);
library(partitions);
library(gtools);

permsum <- function(s,m) if (m==1L) matrix(s) else do.call(rbind,lapply(seq_len(s-m+1L),function(x) unname(cbind(x,permsum(s-x,m-1L)))));
combsum <- function(s,m,l=s) if (m==1L) matrix(s) else do.call(rbind,lapply(seq((s+m-1L)%/%m,min(l,s-m+1L)),function(x) unname(cbind(x,combsum(s-x,m-1L,x)))));
IntegerPartitionsOfLength <- function(n, Lim, combsOnly = TRUE) { a <- 0L:n; k <- 2L; a[2L] <- n; MyParts <- vector("list", length=P(n)); count <- 0L; while (!(k==1L) && k <= Lim + 1L) { x <- a[k-1L]+1L; y <- a[k]-1L; k <- k-1L; while (x<=y && k <= Lim) {a[k] <- x; y <- y-x; k <- k+1L}; a[k] <- x+y; if (k==Lim) { count <- count+1L; MyParts[[count]] <- a[1L:k]; }; }; MyParts <- MyParts[1:count]; if (combsOnly) {do.call(rbind, MyParts)} else {MyParts}; };
GetDecimalReps <- function(s,m) { myPerms <- permutations(m,m); lim <- nrow(myPerms); intParts <- IntegerPartitionsOfLength(s,m,FALSE); do.call(rbind, lapply(intParts, function(x) { unique(t(sapply(1L:lim, function(y) x[myPerms[y, ]]))); })); };

smp <- function(m) m[do.call(order,as.data.frame(m)),];
smc <- function(m) smp(t(apply(m,1L,sort)));

bgoldst.perm <- function(s,m) permsum(s,m)/s;
psidom2 <- function(ss,m) { raw <- do.call(data.table::CJ,rep(list(ss),m)); raw[abs(rowSums(raw)-1)<1e-10,]; };
joseph.perm <- function(s,m) GetDecimalReps(s,m)/s;

bgoldst.comb <- function(s,m) combsum(s,m)/s;
joseph.comb <- function(s,m) IntegerPartitionsOfLength(s,m)/s;

Перестановки

## small scale
s <- 10L; m <- 3L; ss <- seq_len(s-1L)/s;
ex <- smp(bgoldst.perm(s,m));
identical(ex,smp(unname(as.matrix(psidom2(ss,m)))));
## [1] TRUE
identical(ex,smp(joseph.perm(s,m)));
## [1] TRUE

microbenchmark(bgoldst.perm(s,m),psidom2(ss,m),joseph.perm(s,m));
## Unit: microseconds
##                expr      min        lq      mean   median        uq      max neval
##  bgoldst.perm(s, m)  347.254  389.5920  469.1011  420.383  478.7575 1869.697   100
##      psidom2(ss, m)  702.206  830.5015 1007.5111  907.265 1038.3405 2618.089   100
##   joseph.perm(s, m) 1225.225 1392.8640 1722.0070 1506.833 1860.0745 4411.234   100

## large scale
s <- 100L; m <- 4L; ss <- seq_len(s-1L)/s;
ex <- smp(bgoldst.perm(s,m));
identical(ex,smp(unname(as.matrix(psidom2(ss,m)))));
## [1] TRUE
identical(ex,smp(joseph.perm(s,m)));
## [1] TRUE

microbenchmark(bgoldst.perm(s,m),psidom2(ss,m),joseph.perm(s,m),times=5L);
## Unit: seconds
##                expr      min        lq      mean    median        uq       max neval
##  bgoldst.perm(s, m) 1.286856  1.304177  1.426376  1.374411  1.399850  1.766585     5
##      psidom2(ss, m) 6.673545  7.046951  7.416161  7.115375  7.629177  8.615757     5
##   joseph.perm(s, m) 5.299452 10.499891 13.769363 12.680607 15.107748 25.259117     5

## very large scale
s <- 100L; m <- 5L; ss <- seq_len(s-1L)/s;
ex <- smp(bgoldst.perm(s,m));
identical(ex,smp(unname(as.matrix(psidom2(ss,m)))));
## Error: cannot allocate vector of size 70.9 Gb
identical(ex,smp(joseph.perm(s,m)));
## [1] TRUE

microbenchmark(bgoldst.perm(s,m),joseph.perm(s,m),times=1L);
## Unit: seconds
##                expr      min       lq     mean   median       uq      max neval
##  bgoldst.perm(s, m) 28.58359 28.58359 28.58359 28.58359 28.58359 28.58359     1
##   joseph.perm(s, m) 50.51965 50.51965 50.51965 50.51965 50.51965 50.51965     1

Комбинации

## small-scale
s <- 10L; m <- 3L;
ex <- smc(bgoldst.comb(s,m));
identical(ex,smc(joseph.comb(s,m)));
## [1] TRUE

microbenchmark(bgoldst.comb(s,m),joseph.comb(s,m));
## Unit: microseconds
##                expr     min       lq     mean   median       uq      max neval
##  bgoldst.comb(s, m) 161.225 179.6145 205.0898 187.3120 199.5005 1310.328   100
##   joseph.comb(s, m) 172.344 191.8025 204.5681 197.7895 205.2735  437.489   100

## large-scale
s <- 100L; m <- 4L;
ex <- smc(bgoldst.comb(s,m));
identical(ex,smc(joseph.comb(s,m)));
## [1] TRUE

microbenchmark(bgoldst.comb(s,m),joseph.comb(s,m),times=5L);
## Unit: milliseconds
##                expr       min        lq      mean    median       uq       max neval
##  bgoldst.comb(s, m)  409.0708  485.9739  556.4792  591.4774  627.419  668.4548     5
##   joseph.comb(s, m) 2164.2134 3315.0138 3317.9725 3540.6240 3713.732 3856.2793     5

## very large scale
s <- 100L; m <- 6L;
ex <- smc(bgoldst.comb(s,m));
identical(ex,smc(joseph.comb(s,m)));
## [1] TRUE

microbenchmark(bgoldst.comb(s,m),joseph.comb(s,m),times=1L);
## Unit: seconds
##                expr       min        lq      mean    median        uq       max neval
##  bgoldst.comb(s, m)  2.498588  2.498588  2.498588  2.498588  2.498588  2.498588     1
##   joseph.comb(s, m) 12.344261 12.344261 12.344261 12.344261 12.344261 12.344261     1
person bgoldst    schedule 07.06.2016
comment
это довольно однострочный. Проголосовал, но было бы лучше с некоторыми комментариями - person C8H10N4O2; 07.06.2016
comment
Как всегда, очень сложно объяснить, как работает рекурсивная функция. - person Psidom; 07.06.2016
comment
Спасибо за ваше решение. Мне интересно, для m=3, почему решение @Psidom возвращает 4845 строк, в то время как ваше имеет 4851 строк. - person 989; 07.06.2016
comment
Вероятно, это связано с неточностью == и числа с плавающей запятой. Я бы больше доверял ответу @bgoldst, чем моему, поскольку он использует целочисленный тип вместо числового. - person Psidom; 07.06.2016
comment
@bgoldst, очень хороший ответ. Бьюсь об заклад, вы могли бы изменить это, чтобы обеспечить только combinations намного, намного быстрее. +1! - person Joseph Wood; 08.06.2016

Возьмем, к примеру, m=4, подход с интенсивным использованием памяти будет таким:

raw <- data.table::CJ(s,s,s,s)
result <- raw[rowSums(raw) == 1, ]    
head(result)

     V1   V2   V3   V4
1: 0.01 0.01 0.01 0.97
2: 0.01 0.01 0.02 0.96
3: 0.01 0.01 0.03 0.95
4: 0.01 0.01 0.04 0.94
5: 0.01 0.01 0.05 0.93
6: 0.01 0.01 0.06 0.92
person Psidom    schedule 07.06.2016
comment
На самом деле это метод грубой силы. если m=3, nrow(raw) будет 970299, а nrow(result) будет 4845. Разница существенная. Для m>4 вычисление таблицы raw стало бы невозможным. - person 989; 07.06.2016
comment
Я согласен. Это очень интенсивное использование памяти. И мое предложение - попытаться написать рекурсивную функцию для решения проблемы. - person Psidom; 07.06.2016
comment
@Psidom, хороший подход. Я всегда изучаю новые способы применения data.table. - person Joseph Wood; 08.06.2016
comment
Проголосовал за ваши усилия и, что более важно, за выявление ситуации, в которой rowSums() функция не работает. - person 989; 08.06.2016

Вот алгоритм, который вернет чистый combinations (порядок не имеет значения). Он основан на алгоритме целочисленного разбиения, созданном Джеромом Келлехером (ссылка).

library(partitions)
IntegerPartitionsOfLength <- function(n, Lim, combsOnly = TRUE) {
    a <- 0L:n
    k <- 2L
    a[2L] <- n
    MyParts <- vector("list", length=P(n))
    count <- 0L
    while (!(k==1L) && k <= Lim + 1L) {
        x <- a[k-1L]+1L
        y <- a[k]-1L
        k <- k-1L
        while (x<=y && k <= Lim) {a[k] <- x; y <- y-x; k <- k+1L}
        a[k] <- x+y
        if (k==Lim) {
            count <- count+1L
            MyParts[[count]] <- a[1L:k]
        }
    }
    MyParts <- MyParts[1:count]
    if (combsOnly) {do.call(rbind, MyParts)} else {MyParts}
}

system.time(res <- combsum(100L,5L))
 user  system elapsed 
 0.75    0.00    0.77

system.time(a <- IntegerPartitionsOfLength(100, 5))
 user  system elapsed 
 1.36    0.37    1.76

identical(smc(a),smc(res))
[1] TRUE

head(a)
[,1] [,2] [,3] [,4] [,5]
[1,]    1    1    1    1   96
[2,]    1    1    1    2   95
[3,]    1    1    1    3   94
[4,]    1    1    1    4   93
[5,]    1    1    1    5   92
[6,]    1    1    1    6   91

Действительно большой пример (примечание с использованием функции smc, созданной @bgoldst):

system.time(a <- IntegerPartitionsOfLength(100L,6L))
 user  system elapsed 
 4.57    0.36    4.93

system.time(res <- combsum(100L,6L))
 user  system elapsed 
 3.69    0.00    3.71

identical(smc(a),smc(res))
[1] TRUE

## this would take a very long time with GetDecimalReps below

Примечание. IntegerPartitionsOfLength возвращает только combinations определенного набора чисел, а не permutations набора чисел (порядок имеет значение). Например. для набора s = (1, 1, 3) комбинации s точно равны s, а перестановки s таковы: (1, 1, 3), (1, 3, 1), (3, 1, 1).

Если вам нужен ответ, который запрашивает ОП, вам придется сделать что-то вроде этого (это ни в коем случае не лучший способ и не так эффективен, как permsum @bgoldst выше):

library(gtools)
GetDecimalReps <- function(n) {
    myPerms <- permutations(n,n); lim <- nrow(myPerms)
    intParts <- IntegerPartitionsOfLength(100,n,FALSE)
    do.call(rbind, lapply(intParts, function(x) {
        unique(t(sapply(1L:lim, function(y) x[myPerms[y, ]])))
    }))
}

system.time(a <- GetDecimalReps(4L))
 user  system elapsed 
 2.85    0.42    3.28

system.time(res <- combsum(100L,4L))
 user  system elapsed 
 1.35    0.00    1.34

head(a/100)
[,1] [,2] [,3] [,4]
[1,] 0.01 0.01 0.01 0.97
[2,] 0.01 0.01 0.97 0.01
[3,] 0.01 0.97 0.01 0.01
[4,] 0.97 0.01 0.01 0.01
[5,] 0.01 0.01 0.02 0.96
[6,] 0.01 0.01 0.96 0.02

tail(a/100)
[,1] [,2] [,3] [,4]
[156844,] 0.25 0.26 0.24 0.25
[156845,] 0.25 0.26 0.25 0.24
[156846,] 0.26 0.24 0.25 0.25
[156847,] 0.26 0.25 0.24 0.25
[156848,] 0.26 0.25 0.25 0.24
[156849,] 0.25 0.25 0.25 0.25

identical(smp(a),smp(res))  ## using the smp function created by @bgoldst
[1] TRUE

Приведенные выше алгоритмы @bgoldst лучше подходят для обоих типов возврата (т. е. комбинаций/перестановок). Также см. отличные тесты @bgoldst выше. В качестве заключительного замечания: вы можете легко изменить IntegerPartionsOfLength, чтобы получить все комбинации 1:100, сумма которых равна 100 для k <= m, просто изменив k==Lim на k <= Lim, а также установив combsOnly = FALSE для возврата списка. Ваше здоровье!

person Joseph Wood    schedule 07.06.2016
comment
спасибо, но ваша функция работает неправильно. возьмите nrow из res и a. Вы заметите, почему это так. - person 989; 08.06.2016
comment
@ m0h3n, перечитай мой ответ еще раз. Я знаю, что a (комбинации) и res (перестановки) — это не одно и то же. См. ссылку, которую я разместил выше в комментариях к вашему вопросу. - person Joseph Wood; 08.06.2016
comment
Кстати, ваше решение не делает того, о чем задается вопрос. Перечитайте вопрос и примите ответ. - person 989; 08.06.2016
comment
@ m0h3n, у меня была ошибка копирования/вставки (извините за это). Теперь это должно работать. Между прочим, я знаю, что решение, предоставленное bgoldst, намного лучше, я просто показывал сообществу другой взгляд на проблему. Теория целочисленных разделов — невероятно богатая тема, которой не уделяется достаточного внимания. Во всяком случае, это была невероятно забавная задача, и, как всегда, ура!! - person Joseph Wood; 08.06.2016
comment
Ваши результаты неверны в обоих случаях (т. е. res НЕ РАВНО a). Также забавно, что вы тестируете два алгоритма, которые НЕ выполняют одну и ту же работу. - person 989; 08.06.2016
comment
@JosephWood Я ценю ваш ответ и комментарии. Я сильно отредактировал свой ответ на основе ваших идей. К сожалению, поскольку я использовал имя combsum() для своей исходной функции перестановки, я, вероятно, вызвал много путаницы. И даже более того, потому что в своем отредактированном ответе я переименовал его в permsum(), а затем написал новый combsum() для создания комбинаций. Поэтому, пожалуйста, обратите внимание на мои изменения и извините за путаницу. Проголосовал за ваш ответ. - person bgoldst; 08.06.2016
comment
@bgoldst, я обновлю как можно скорее. Ваши алгоритмы просто потрясающие!!! - person Joseph Wood; 08.06.2016
comment
@ m0h3n, res и a одинаковы (они упорядочены по-разному) - person Joseph Wood; 08.06.2016
comment
С ОБНОВЛЕННОЙ функцией combsum из принятого ответа да. Теперь они дают идентичные результаты. Раньше вы сравнивали свои результаты с permsum, и ваш вывод был неправильным и поэтому был намного быстрее;) - person 989; 08.06.2016
comment
Кстати, теперь я убежден, что проголосую за ваш ответ, и я действительно это сделал. - person 989; 08.06.2016