Как сделать вместо 40 циклов 1?

Мой код складывает два соседних числа и делит на 2, в результате чего список каждый раз уменьшается на 1. Как сделать вместо 40 циклов один?

array = '1101111011010101110101010010011010100100'
array = [i + 1 for i in range(len(array)) if array[i] == '1']
count0, count1, count = 0, 1, []
print(len(array))
for i in array:
    count.append((array[count0] + array[count1]) / 2)
    count0 += 1
    print(count0)
    if count0 == len(array) - 1:
        break
    else:
        count1 += 1

Вот на простом примере:

a = [1, 2, 4, 5, 6]
a = [1.5, 3, 4.5, 5.5]
a = [2.25, 3.75, 5]
a = [3, 4.375]
a = [3.6875]

Это не среднее число списка - должно получиться 3.6


Ответы (1 шт):

Автор решения: MBo

В списке длиной N+1 (заметьте +1) нужно сложить числа с коэффициентами для K-го числа (нумерация с нуля) С(N, K) / 2^(N), т.е.

Result = Sum(k=0..N){A[K]*C(N,K)/2^N} = 1/2^N * Sum(k=0..N){C(N,K)*A[K]}

где С(N, K) - биномиальные коэффициенты

Например, для списка длиной 5 будут коэффициенты из 4-го ряда треугольника Паскаля 1,4,6,4,1, разделённые на 16

def cnk(n, k):
    k = min(k, n - k)
    if k <= 0:
        return 1 if k == 0 else 0
    res = 1
    for i in range(k):
        res = res * (n - i) // (i + 1)
    return res

def freaksum(a):
    n = len(a) - 1
    return sum([a[k] * cnk(n, k) for k in range(n+1)]) / 2**n

l = [1, 2, 4, 5, 6]
print(freaksum(l))

>>> 3.6875

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

P.S. Между прочим, всё это действие по большому счёту эквивалентно наложению Гауссова фильтра

для проверки:

def repsum(a):
    while len(a) > 1:
        b = []
        for i in range(len(a) - 1):
            b.append((a[i] + a[i + 1]) / 2)
        a = b[:]
    return a[0]

l = [1,2,3,4,8,9,10,13,14,15,19]
print(freaksum(l))
print(repsum(l))
→ Ссылка