@lutokris

Как еще чуть ускорить алгоритм?

Всем добра. Решаю задачку по определению расстояния до ближайшего нуля в элементах списка. Типа допустим есть список вида 5 3 0 2 0 3 а на выходе должно получиться 2 1 0 1 0 1. Я конечно пошел по немного "странному" пути - но решил пойти до конца и сделать в таком виде. У меня конечно все работает, трачу минимум памяти, но не укладываюсь в отведенное время на 0.77 секунд. Никак не могу понять в каком участке кода что мне оптимизировать чтобы выиграть еще 0.77 секунд(

def know_range(n):
    result = []
    half = n // 2
    if n == 1:
        result.append(1)
        return result
    result += [x for x in range(1, half+1)]
    if n & 1: # если нечетный
        result.append(half+1)
    result += [x for x  in range(half, 0, -1)]
    return result


def file_read_to_byte_discrete(f_path):
    res_arr = 0b0
    len_arr = 0b0
    n = 0
    with open(f_path) as f:
        n = int(f.readline()) + 1
        len_arr = 0b1 << n
        buf = ""
        #for i in f.read():
        i = " "
        while i != "":
            i = f.read(1)
            if i != " " and i != "":
                buf+= i
            else:
                if int(buf) | 0:
                    res_arr = res_arr | 1
                else:
                    res_arr = res_arr | 0
                res_arr = res_arr << 1
                buf = ""
        f.close()
    res_arr = len_arr + res_arr
    res_arr = res_arr >> 1
    return res_arr


byte_arr = file_read_to_byte_discrete('input.txt')
n = len(bin(byte_arr)) - 3
result_arr = []
number = 0

# проверим левый край если там не ноль
left_null = 0
if ((byte_arr >> n-1) & 1):
    number = 0
    for i in range(n-1, -1, -1):
        if not ((byte_arr >> i) & 1): # если найдем нолик выходим
            if number != 0:
                left_null = number
            else:
                left_null = 1
            break
        else:
            number += 1
    if number != 0:
        for i in range(number, 0, -1):
            result_arr.append(i)

number = 0
for i in range(n-1-left_null, -1, -1):
    if not ((byte_arr >> i) & 1):
        # найден нолик
        if number != 0:
            result_arr = result_arr + know_range(number)
        result_arr.append(0)
        number = 0
    else:
        number += 1
# проверим правый край если там не ноль просто печатаем номера
if (byte_arr & 1):
    result_arr += [x for x in range(1, number+1)]
    

for el in result_arr:
    print(el, end=" ")
  • Вопрос задан
  • 1119 просмотров
Решения вопроса 1
Vindicar
@Vindicar
RTFM!
А не проще ли будет сделать в два прохода.
Прямым проходом делаем так: если 0, то счетчик в ноль, иначе счетчик + 1 и элемент приравниваем счетчику.
Потом то же самое делаем обратным проходом, но изменяем элемент только если он больше счетчика.
Отсюда получаем:
lst=[5, 3, 0, 2, 0, 3, 8, 2, 9, 7, 0, 0, 7, 1, 5, 3]
L = len(lst)
# если в начале списка не 0, то мы сможем задать корректные значения только на обратном ходе
# так что ставим заведомо большее значение счетчика
counter = len(lst) if lst[0] != 0 else 0
for i in range(0, L):
  if lst[i] == 0:
    counter = 0
  else:
    counter += 1
    lst[i] = counter
# обратный ход
counter = lst[-1] - 1 #чтобы не запороть последние элементы
for i in range(L-1, -1, -1):
  if lst[i] == 0:
    counter = 0
  else:
    counter += 1
    lst[i] = min(counter, lst[i])

print(lst)
# [2, 1, 0, 1, 0, 1, 2, 3, 2, 1, 0, 0, 1, 2, 3, 4]

Правда, это даст некорректный результат, если список не содержит нулей. Но это можно обнаружить в ходе первого прохода.
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
@dmshar
А вот так не быстрее будет?
(Пример специально усложнил)

lst=[5, 3, 0, 2, 0, 3, 8, 2, 9, 7, 0, 0,7,1, 5, 3]

l=[i for i,v in enumerate(lst) if v == 0]
m_l=[]
for i,ls in enumerate(lst):
    m=len(lst)
    for j in l:
        if m>abs(i-j):
            m=abs(i-j)
    m_l.append(m)
print (m_l)


Результат:
[2, 1, 0, 1, 0, 1, 2, 3, 2, 1, 0, 0, 1, 2, 3, 4]

Если надо еще ускорить - можно порождать список m_l сразу, т.е.
m_l=[len(lst)]*len(lst)
и
m_l.append(m)
заменить на
m_l[i]=m
За счет статики работы с списком должно бы получиться еще быстрее.
Ответ написан
Alexandroppolus
@Alexandroppolus
кодир
Путь действительно странный. А главное, есть какие-то непонятные действия с массивом, которые, возможно, приводят к копированию, например result_arr = result_arr + know_range(number)

Проще всего было бы из строки получить массив с возрастающими ненулевыми интервалами, а потом пройтись по нему справа налево и "посрезать уголки":

строка "5 3 0 4 6 1 5 0 9 2 3 0"
массив после первого обхода: [inf, inf, 0, 1, 2, 3, 4, 0, 1, 2, 3, 0]
после обратного обхода, тот же массив: [2, 1, 0, 1, 2, 2, 1, 0, 1, 2, 1, 0]
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы