R: Генерация всех перестановок весов N, кратных P

Мне нужно создать функцию (в R), которая:
- учитывая N возможных переменных для присвоения весов;
- создает все возможные перестановки весов (в сумме до 100%);< br> — при условии, что веса должны быть кратны P (обычно 1 %).

Очевидно, так как N и P обратно пропорциональны - т.е. я не могу указать N=7, а P=0,4. Однако я хотел бы иметь возможность указывать только целочисленные решения, т.е. P=0,01.

Извините, если это общеизвестная проблема — я не математик, и я искал, используя известные мне термины, но не нашел ничего достаточно близкого.

Я бы опубликовал код, который я написал, но... он не впечатляет и не проницателен.

Спасибо за любую помощь!


person Daniel Egan    schedule 31.07.2011    source источник
comment
Можете ли вы привести пример с маленькими N и P? Я не совсем понимаю, что вы подразумеваете под перестановкой весов. Без ограничений на веса это могут быть реальные значения между [0,1]. Множество таких значений несчетно. Или кратные P подразумевают, что это должны быть целые кратные?   -  person Iterator    schedule 31.07.2011
comment
Описание приложения также может помочь. Это похоже на поиск всех точек на целочисленной решетке с некоторыми ограничениями. Если это сводится к стандартной задаче о рюкзаке, удачи.   -  person Iterator    schedule 31.07.2011
comment
Я бы предложил опубликовать код в любом случае.   -  person Roman Luštrik    schedule 31.07.2011


Ответы (2)


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

Поскольку вы хотите, чтобы веса в сумме равнялись 1, вам нужно, чтобы 1/p было целым числом, которое в следующем коде равно sumparts; это не зависит от количества весов. Получив композиции, вы можете умножить их на p, т. е. разделить на n, чтобы получить веса.

R имеет пакет partitions для создания таких композиций или разделов с ограниченным доступом. Следующий код не требует пояснений: каждый столбец в матрице представляет собой набор весов. Я взял семь весов и p = 0,1 или 10%, а запрещенные веса равны 0: это дает 84 возможности; допустимый вес 0 будет означать 8008 возможностей. При p = 0,01 или 1% было бы 1 120 529 256 возможностей без весов 0 и 1 705 904 746 с весами. Если порядок не имеет значения, используйте restrictedparts вместо compositions.

> library(partitions)
> numparts <- 7  # number of weights
> sumparts <- 10  # reciprocal of p
> weights <- compositions(n=sumparts, m=numparts, include.zero=FALSE)/sumparts
> weights

[1,] 0.4 0.3 0.2 0.1 0.3 0.2 0.1 0.2 0.1 0.1 0.3 0.2 0.1 0.2 0.1 0.1 0.2 0.1 0.1
[2,] 0.1 0.2 0.3 0.4 0.1 0.2 0.3 0.1 0.2 0.1 0.1 0.2 0.3 0.1 0.2 0.1 0.1 0.2 0.1
[3,] 0.1 0.1 0.1 0.1 0.2 0.2 0.2 0.3 0.3 0.4 0.1 0.1 0.1 0.2 0.2 0.3 0.1 0.1 0.2
[4,] 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.2 0.2 0.2 0.2 0.2 0.2 0.3 0.3 0.3
[5,] 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1
[6,] 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1
[7,] 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1

[1,] 0.1 0.3 0.2 0.1 0.2 0.1 0.1 0.2 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.1 0.3 0.2 0.1
[2,] 0.1 0.1 0.2 0.3 0.1 0.2 0.1 0.1 0.2 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.1 0.2 0.3
[3,] 0.1 0.1 0.1 0.1 0.2 0.2 0.3 0.1 0.1 0.2 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.1 0.1
[4,] 0.4 0.1 0.1 0.1 0.1 0.1 0.1 0.2 0.2 0.2 0.3 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.1
[5,] 0.1 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.3 0.3 0.3 0.3 0.4 0.1 0.1 0.1
[6,] 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.2 0.2 0.2
[7,] 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1

[1,] 0.2 0.1 0.1 0.2 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.1 0.1 0.3
[2,] 0.1 0.2 0.1 0.1 0.2 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.1 0.1
[3,] 0.2 0.2 0.3 0.1 0.1 0.2 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.1
[4,] 0.1 0.1 0.1 0.2 0.2 0.2 0.3 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.1 0.2 0.1 0.1 0.1
[5,] 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.2 0.2 0.2 0.2 0.3 0.1 0.1 0.1 0.1 0.2 0.1 0.1
[6,] 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.3 0.3 0.3 0.3 0.3 0.4 0.1
[7,] 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.2

[1,] 0.2 0.1 0.2 0.1 0.1 0.2 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.1
[2,] 0.2 0.3 0.1 0.2 0.1 0.1 0.2 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.1 0.2 0.1 0.1 0.1
[3,] 0.1 0.1 0.2 0.2 0.3 0.1 0.1 0.2 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.1 0.2 0.1 0.1
[4,] 0.1 0.1 0.1 0.1 0.1 0.2 0.2 0.2 0.3 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.1 0.2 0.1
[5,] 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.2 0.2 0.2 0.2 0.3 0.1 0.1 0.1 0.1 0.2
[6,] 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.2 0.2 0.2 0.2 0.2
[7,] 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2

[1,] 0.1 0.2 0.1 0.1 0.1 0.1 0.1 0.1
[2,] 0.1 0.1 0.2 0.1 0.1 0.1 0.1 0.1
[3,] 0.1 0.1 0.1 0.2 0.1 0.1 0.1 0.1
[4,] 0.1 0.1 0.1 0.1 0.2 0.1 0.1 0.1
[5,] 0.1 0.1 0.1 0.1 0.1 0.2 0.1 0.1
[6,] 0.3 0.1 0.1 0.1 0.1 0.1 0.2 0.1
[7,] 0.2 0.3 0.3 0.3 0.3 0.3 0.3 0.4
person Henry    schedule 31.07.2011
comment
Привет @Henry, это работает отлично. И быстро! Спасибо за помощь. - person Daniel Egan; 02.08.2011

EDIT: функция обновлена, так как она дважды давала некоторые результаты.

Вы можете попробовать эту функцию, основанную на рекурсивном вычислении. Он даст вам все возможные комбинации, независимо от порядка. Я сделал это таким образом, иначе вы получите несколько строк со всеми возможными перестановками.

Расчет основан на целых числах. Минимальный вес P устанавливается равным 1, а Pint становится количеством единиц веса, которые можно разделить. max.W будет максимальным количеством единиц, которое может быть присвоено одной переменной.

Алгоритм выглядит следующим образом:

  • если N=2, то составить все возможные комбинации для заданного минимального и максимального веса.

  • если N > 2, применить этот алгоритм для N = 1 к потолку (макс. вес / N), с максимальным весом, указанным как текущий максимальный вес +1 минус N, и минимальным весом как N.

Это дает вам все возможные комбинации целых чисел. Умножение на P дает исходные веса.

Или в функции:

myfunc <- function(N,P){
  if(100%%(P*100) !=0){
    stop("100% cannot be divided in portions of P")
  }
  Pint <- 100/(P*100)
  max.W <- Pint- N + 1

  combs <- function(n,max.w,min){
    mw <- max.w + 1

    if(n==2){

      w <- seq.int(min,floor((mw)/2))
      out <- cbind(w,mw-w)

    } else if (n > 2){

      x <- lapply(1:ceiling(max.w/n),function(i){

        newcombs <- combs(n-1,mw-i,i)
        cbind(newcombs,rep(i,nrow(newcombs)))

      })

      out <- do.call("rbind",x)
    }
    colnames(out) <-rownames(out) <- NULL
    out
  }  
  res <- combs(N,max.W)
  apply(res,1,sort)*P
}

Это дает комбинации в столбцах матрицы:

> Y <- myfunc(3,0.1)
> Y
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
[1,]  0.1  0.1  0.1  0.1  0.2  0.2  0.2  0.3
[2,]  0.1  0.2  0.3  0.4  0.2  0.3  0.4  0.3
[3,]  0.8  0.7  0.6  0.5  0.6  0.5  0.4  0.4

Имейте в виду! с приведенным вами тестовым набором (7 переменных, скачки 0,01) вы будете очень долго вычислять огромное количество возможностей. При N=7 и P=0,04 у вас уже есть 3555 возможных комбинаций. При N=0,2 получается 336 443 возможности. И вы должны учитывать все возможные перестановки этих комбинаций, если это то, что вам нужно.

person Joris Meys    schedule 31.07.2011
comment
У вас есть несколько дубликатов: столбцы 2 и 5, 3 и 9, 4 и 12, 7 и 10, 8 и 13 и 11 и 14. - person Henry; 01.08.2011
comment
@ Генри: поймал их, теперь проблема решена. Во-вторых, вы, по-видимому, уже решили проблему, используя более простой подход. Ну, мне было весело играть с этим. Играть с рекурсией всегда весело :-) - person Joris Meys; 01.08.2011