Как эффективно вычислить строку в треугольнике Паскаля?
мне интересно найти n-ю строку треугольника Паскаля (не конкретный элемент, а всю строку). Как это сделать наиболее эффективно?
Я думал об обычном способе построения треугольника, суммируя соответствующие элементы в строке выше, которые будут принимать:
1 + 2 + .. + n = O(n^2)
другим способом может быть использование формулы комбинации определенного элемента:
c(n, k) = n! / (k!(n-k)!)
для каждого элемента в строке, которая я думаю, будет потратьте больше времени на первый метод в зависимости от способа вычисления комбинации. Есть идеи?
11 ответов
>>> def pascal(n):
...   line = [1]
...   for k in range(n):
...     line.append(line[k] * (n-k) / (k+1))
...   return line
... 
>>> pascal(9)
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
это использует следующий идентификатор:
C(n,k+1) = C(n,k) * (n-k) / (k+1)
так что вы можете начать с C(n,0) = 1 а затем вычислить остальную часть строки, используя это удостоверение, каждый раз умножая предыдущий элемент на (n-k) / (k+1).
одну строку можно рассчитать следующим образом:
First compute 1.               -> N choose 0
Then N/1                       -> N choose 1
Then N*(N-1)/1*2               -> N choose 2
Then N*(N-1)*(N-2)/1*2*3       -> N choose 3
.....
обратите внимание, что вы можете вычислить следующее значение из предыдущего значения, просто мультипилируя на одно число, а затем деля на другое число.
Это можно сделать в одном цикле. Пример python.
def comb_row(n):
   r = 0
   num = n
   cur = 1
   yield cur
   while r <= n:
      r += 1  
      cur = (cur* num)/r
      yield cur
      num -= 1
наиболее эффективным подходом было бы:
std::vector<int> pascal_row(int n){
    std::vector<int> row(n+1);
    row[0] = 1; //First element is always 1
    for(int i=1; i<n/2+1; i++){ //Progress up, until reaching the middle value
        row[i] = row[i-1] * (n-i+1)/i;
    }
    for(int i=n/2+1; i<=n; i++){ //Copy the inverse of the first part
        row[i] = row[n-i];
    }
    return row;
}
вот быстрый пример, реализованный в go-lang, который вычисляет с внешних краев строки и работает до середины, назначая два значения с одним вычислением...
package main
import "fmt"
func calcRow(n int) []int {
    // row always has n + 1 elements
    row := make( []int, n + 1, n + 1 )
    // set the edges
    row[0], row[n] = 1, 1
    // calculate values for the next n-1 columns
    for i := 0; i < int(n / 2) ; i++ {
        x := row[ i ] * (n - i) / (i + 1)
        row[ i + 1 ], row[ n - 1 - i ] = x, x
    }
    return row
}
func main() {
    for n := 0; n < 20; n++ {
        fmt.Printf("n = %d, row = %v\n", n, calcRow( n ))
    }
}
выход для 20 итераций занимает около 1/4 миллисекунды для запуска...
n = 0, row = [1]
n = 1, row = [1 1]
n = 2, row = [1 2 1]
n = 3, row = [1 3 3 1]
n = 4, row = [1 4 6 4 1]
n = 5, row = [1 5 10 10 5 1]
n = 6, row = [1 6 15 20 15 6 1]
n = 7, row = [1 7 21 35 35 21 7 1]
n = 8, row = [1 8 28 56 70 56 28 8 1]
n = 9, row = [1 9 36 84 126 126 84 36 9 1]
n = 10, row = [1 10 45 120 210 252 210 120 45 10 1]
n = 11, row = [1 11 55 165 330 462 462 330 165 55 11 1]
n = 12, row = [1 12 66 220 495 792 924 792 495 220 66 12 1]
n = 13, row = [1 13 78 286 715 1287 1716 1716 1287 715 286 78 13 1]
n = 14, row = [1 14 91 364 1001 2002 3003 3432 3003 2002 1001 364 91 14 1]
n = 15, row = [1 15 105 455 1365 3003 5005 6435 6435 5005 3003 1365 455 105 15 1]
n = 16, row = [1 16 120 560 1820 4368 8008 11440 12870 11440 8008 4368 1820 560 120 16 1]
n = 17, row = [1 17 136 680 2380 6188 12376 19448 24310 24310 19448 12376 6188 2380 680 136 17 1]
n = 18, row = [1 18 153 816 3060 8568 18564 31824 43758 48620 43758 31824 18564 8568 3060 816 153 18 1]
n = 19, row = [1 19 171 969 3876 11628 27132 50388 75582 92378 92378 75582 50388 27132 11628 3876 969 171 19 1]
простой способ вычислить это, заметив, что элемент следующей строки может быть вычислен как сумма двух последовательных элементов в предыдущей строке.
[1, 5, 10, 10, 5, 1]
[1, 6, 15, 20, 15, 6, 1]
6 = 5 + 1, 15 = 5 + 10, 1 = 1 + 0 и 20 = 10 + 10. Это дает простой алгоритм для вычисления следующей строки из предыдущей.
def pascal(n):
    row = [1]
    for x in xrange(n):
        row = [l + r for l, r in zip(row + [0], [0] + row)]
    # print row
    return row
print pascal(10)
в программировании Scala: я бы сделал это так просто:
def pascal(c: Int, r: Int): Int = c match {
    case 0 => 1
    case `c` if c >= r => 1
    case _ => pascal(c-1, r-1)+pascal(c, r-1)
}
Я бы назвал это внутри этого:
for (row <- 0 to 10) {
    for (col <- 0 to row)
        print(pascal(col, row) + " ")
    println()
}
в результате:
. 
              1 
             1 1 
            1 2 1 
           1 3 3 1 
          1 4 6 4 1 
        1 5 10 10 5 1 
       1 6 15 20 15 6 1 
      1 7 21 35 35 21 7 1 
     1 8 28 56 70 56 28 8 1 
   1 9 36 84 126 126 84 36 9 1 
1 10 45 120 210 252 210 120 45 10 1
чтобы объяснить шаг за шагом:
Шаг 1: мы удостоверяемся, что если наша колонка первая, мы всегда возвращаем Рисунок 1.
Шаг 2: так как в каждой X-й строке есть X число столбцы. Итак, мы говорим, что; последний столбец X больше или равен X-й строке, затем возвращаемый Рисунок 1.
Шаг 3: в противном случае, мы получим сумму повторяется Паскаль столбца перед текущим и строки перед текущей ; и Паскаль столбца и строки перед текущей.
Удачи.
самый эффективный способ вычисления строки в треугольнике Паскаля - это свертка. Сначала мы выбрали вторую строку (1,1) ядром, а затем, чтобы получить следующую строку, нам нужно только свернуть строку curent с ядром.
таким образом, свертка ядра со второй строкой дает третью строку [1 1]*[1 1] = [1 2 1], свертка с третьей строкой дает четвертую [1 2 1]*[1 1] = [1 3 3 1] и так далее
это функция в julia-lang (очень похожая на matlab):
function binomRow(n::Int64)
baseVector = [1] #the first row is equal to 1. 
kernel = [1,1]   #This is the second row and a kernel. 
row = zeros(n)
for i = 1 : n
    row = baseVector 
    baseVector = conv(baseVector, kernel) #convoltion with kernel
end
return row::Array{Int64,1}
end
в Ruby следующий код распечатает определенную строку треугольника Паскаля, которую вы хотите:
def row(n)
  pascal = [1]
  if n < 1
    p pascal
    return pascal
  else
    n.times do |num|
      nextNum = ((n - num)/(num.to_f + 1)) * pascal[num]
      pascal << nextNum.to_i
    end
  end
  p pascal
end
где row(0) возвращает [1] и row(5) возвращает [1, 5, 10, 10, 5, 1]
вот еще один лучший и простой способ динамического проектирования треугольника Паскаля с помощью VBA.
`1
11
121
1331
14641`
`Sub pascal()
Dim book As Excel.Workbook
Dim sht As Worksheet
Set book = ThisWorkbook
Set sht = book.Worksheets("sheet1")
a = InputBox("Enter the Number", "Fill")
For i = 1 To a
    For k = 1 To i
        If i >= 2 And k >= 2 Then
            sht.Cells(i, k).Value = sht.Cells(i - 1, k - 1) + sht.Cell(i-  1, k)
        Else
            sht.Cells(i, k).Value = 1
        End If
    Next k
Next i
End Sub`
я использовал Ti-84 Plus CE
использование –> в строке 6-кнопка сохранить значение
Forloop syntax is 
:For(variable, beginning, end [, increment])
:Commands
:End
nCr syntax is 
:valueA nCr valueB
индексы списка начинаются с 1, поэтому я установил его в R+1
N= row
R= column
PROGRAM: PASCAL
:ClrHome
:ClrList L1
:Disp "ROW
:Input N
:For(R,0,N,1)
:N nCr R–>L1(R+1)
:End
:Disp L1
Это самый быстрый способ, который я могу придумать, чтобы сделать это в программировании (с ti 84), но если вы хотите, чтобы иметь возможность вычислить строку с помощью пера и бумаги, то просто нарисовать треугольник причина делать факториалы боль!
вот решение o (n) пространственной сложности в Python:
def generate_pascal_nth_row(n):
    result=[1]*n
    for i in range(n):
        previous_res = result.copy()
        for j in range(1,i):
            result[j] = previous_res[j-1] + previous_res[j]
    return result
print(generate_pascal_nth_row(6))