Как ускорить вложенный цикл с помощью поиска по индексу PYTHON - PullRequest
0 голосов
/ 25 мая 2018

я получаю значения из книги заказов в виде списка:

list1 = [...,'ethbtc', '0.077666', '10', '0.077680', '15',...]
------------------------ ^ символ ----- ^ значение ----- ^ количество--

В этом списке около 100 символов и 40 значений для каждого символа.Они всегда в одном и том же порядке.
Я бы хотел узнать, по какой максимальной цене моя система покупает в данный момент, если я заплачу, скажем, 100% моего баланса.

Так что, если я хочу купить 11ETH на 0.077666, реальная цена будет 0.077680, потому что по первой цене доступно только 10 ETH.
Я не хочу получать среднее значение, потому что в данный момент это будет слишком много

В моем коде есть вложенный код.для цикла loop и через 2 списка:

  1. coinlist = где все 100 символов перечислены так: symbollist = [ethbtc, eoseth,...]
  2. список индексов, называемый a, поскольку значения и количества всегдав том же месте
    a = ['1', '3', '5', ...]

Мой код:

for symbolnow in symbollist:
sumlist = []
    for i in a:
        quantity = float(list1[list1.index(symbolnow) + (i+1)] if symbolnow in list1 else 0)
        sumlist.append(quantity)
        if sum(sumlist) > mycurrentbalance:
            maxvalue = float(list1[list1.index(symbolnow) + i] if symbolnow in list1 else -1)
            break
        else:
            maxvalue = -1

Итак, что делает этот код:
1) цикл по каждому символу в списке символов
2) для каждого найденного символа я ищу доступное количество
3) если мой баланс (т.е. 10 ETH) меньше, чем qty, цикл прерывается
4) если не продолжает искать и суммировать каждый кол-во в суммеt, пока не будет достаточно.

Код работает, как задумано, но не так быстро.Как и ожидалось, list1.index занимает много времени ..

Вопрос
Как будет работать более быстрый код.Лучше ли понимание списка в этом сценарии или даже регулярное выражение?Мой код очень уродлив?

Заранее спасибо!

РЕДАКТИРОВАТЬ:
, чтобы уточнить вход и желаемый вывод, пример:

list1 = [...,'ethbtc', '0.077666', '1', '0.077680', '1.5', '0.077710', '3', '0.078200', '4',...]
mycurrentbalance = 5.5 <- баланс в ETH <br>каждая третья запись в list1 - это количество в ETH, поэтому в списке это будет ['1', '1.5', '3', '4']

, поэтому, если я хочу продать всемой ETH (в данном случае 5.5) максимальное значение будет «0.077710»

list1 содержит 100 символов, поэтому до и после 'ethbtc' существуют другие значения величин и символы

Ответы [ 3 ]

0 голосов
/ 25 мая 2018

В вашем случае, я думаю, использование объекта среза поможет с вашим циклом «a», если есть фиксированный интервал.Вы можете сохранить фрагмент списка на объекте, как показано ниже (также 1 или 2 других совета).Я согласен с пользователем выше, что если у вас есть возможность предварительно обработать эти входные данные, то вы действительно должны.Я бы порекомендовал использовать для этого библиотеку pandas, потому что она очень быстрая, но словари также позволят хэшировать значения.

input_data = ['ethbtc', '0.0776666', '10', '0.077680', '15']  # Give your variables meaningful names

length = 20 # a variable to store how long a list of values is for a particular symbol.

for symbol in symbollist: # Use meaningful names if loops too
    start = input_data.index(symbol)  # break up longer lines
    # Some exception handling here
    indxs = slice(start: start+length:2) # python lets you create slice objects
    quantities = [float(number) for number in input_data[indxs]]

    if sum(quantities) > mycurrentbalance:
        # Whatever code here
        ....
0 голосов
/ 27 мая 2018

В дополнение к ответу от пользователя 3080953 вам необходимо предварительно обработать данные не только потому, что это будет более эффективно, но и потому, что это поможет вам справиться со сложностью.Здесь вы делаете две вещи одновременно: расшифровываете свой список и используете данные.Сначала декодируйте, затем используйте.

Целевой формат должен быть, по моему мнению:

prices_and_quantities_by_symbol = {
    'ethbtc': {
        'prices':[0.077666, 0.077680, 0.077710, 0.078200], 
        'quantities':[1, 1.5, 3, 4]
    }, 
    'btceth': {
        ...
    }, 
...}

Теперь вам просто нужно сделать:

for symbol, prices_and_quantities in prices_and_quantities_by_symbol.items(): # O(len(symbol_list))
    total = 0
    for p, q in zip(prices_and_quantities["prices"], prices_and_quantities["quantities"]): # O(len(quantities))
        total += q # the running sum
        if total >= my_current_balance:
            yield symbol, p # this will yield the symbol and the associated max_value
            break

Какполучить данные в целевом формате?Просто переберите список и, если найдете символ, начинайте хранить значения и количества до следующего символа:

prices_and_quantities_by_symbol = {}
symbol_set = (symbol_list) # O(len(symbol_list))
for i, v in enumerate(list1): # O(len(list1))
    if v in symbol_set:  # amortized O(1) lookup
        current_prices = []
        current_quantities = []
        current_start = i+1
        prices_and_quantities_by_symbol[v] = {
            'prices':current_prices, 
            'quantities':current_quantities
        }
    else: # a value or a quantity
        (current_prices if (i-current_start)%2==0 else current_quantities).append(float(v))

У вас есть небольшая, но интересная оптимизация, особенно если ваш список величин / значенийдлинныеСохраняйте не количество, а текущую сумму:

prices_and_running_total_by_symbol = {
    'ethbtc': {
        'prices':[0.077666, 0.077680, 0.077710, 0.078200], 
        'running_total':[1, 2.5, 5.5, 9.5]
    }, 
    'btceth': {
        ...
    }, 
...}

Теперь вы можете очень быстро найти значение max_value, используя bisect.Код становится более простым для понимания, поскольку bisect.bisect_left(rts, my_current_balance) возвращает индекс первого промежуточного итога >= my_current_balance:

for symbol, prices_and_running_totals in prices_and_running_totals_by_symbol.items(): # O(len(symbol_list))
    ps = prices_and_running_totals["prices"]
    rts = prices_and_running_totals["running_total"]
    i = bisect.bisect_left(rts, my_current_balance) # O(log(len(rts)))
    yield symbol, ps[i] # this will yield the symbol and the associated max_value

Чтобы построить промежуточный итог, вы должны по-разному обрабатывать цены и количества.:

# O(len(list1))
...
if v in symbol_set:  # amortized O(1) lookup*
    ...
elif (i-current_start)%2==0:
    current_prices.append(float(v))
else:
    current_running_totals.append((current_running_totals[-1] if current_running_totals else 0.0) + float(v))

Поместите все в функции (или, лучше, методы класса):

prices_and_running_totals_by_symbol = process_data(list1)
for symbol, max_value in symbols_max_values(prices_and_running_totals_by_symbol, my_current_balance):
    print(symbol, max_value)

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

0 голосов
/ 25 мая 2018

Preprocess list1 и сохраните его в формате dict.Это означает, что вы перебираете list1 только один раз, а не каждый раз, когда работает ваш внутренний цикл.

price_dict = {'ethbtc': ['0.077666', '10', '0.077680', '15'], 'btceth': [...], ...}

Вместо итерации по a, итерации по range (Python 3) или xrange (Python 2).Это будет использовать итератор вместо списка и сделает ваш код более гибким.

range(0, len(price_dict[symbol]), 2)
...