Страницы

Поиск по вопросам

Показаны сообщения с ярлыком списки. Показать все сообщения
Показаны сообщения с ярлыком списки. Показать все сообщения

четверг, 9 апреля 2020 г.

Как рекурсивно создать двунаправленный список зная только количество элементов, Java

#java #рекурсия #структуры_данных #списки #связывание_данных

                    
Есть DDS DoubleNode, которая является двунаправленным списком:

public class DoubleNode {
public int value;
public DoubleNode prev;
public DoubleNode next;

public DoubleNode(int value, DoubleNode prev, DoubleNode next) {
    this.value = value;
    this.prev = prev;
    this.next = next;
}


а вот что я пытаюсь сделать, так это построить такую Node, зная только количество
элементов в ней (значения элементов выбираются случайно). При всем при этом построить
хочу рекурсивно, сваял вот такой метод:

    public static DoubleNode doubleNodeGenRec (int length) {
    return (length == 0) ? null : new DoubleNode((int)(Math.random()*10), null, doubleNodeGenRnd(--length));
}


как новому элементу передать ссылку на DoubleNode prev;?

выходит, что строится Node в одну сторону, т.к. в методе туда передается просто null,
и в результате выходит просто связанный список. чем заменить этот null?
    


Ответы

Ответ 1



public DoubleNode(int value) { this.value = value; } public static DoubleNode doubleNodeGen(int length) { return doubleNodeGenRec(length, null); } private static DoubleNode doubleNodeGenRec(int length, DoubleNode prev) { if (length == 0) { return null; } int value = (int) (Math.random() * 10); DoubleNode node = new DoubleNode(value); node.prev = prev; node.next = doubleNodeGenRec(length - 1, node); return node; }

вторник, 31 марта 2020 г.

Рекурсивная функция для поиска пути в List-ах

#c_sharp #рекурсия #списки #словари


Есть n-ое количество элементов в словаре:

Dictionary> dict = new Dictionary>();


В List лежат ключи связующих словарей. То есть, например, словарь с ключом '1' связывается
со словарями с ключами 1, 6, 8, 10. А словарь с ключом 90 связывается со словарями
с ключами 8, 3, 92, 138. 

dict[1] = new List(){ 87, 6, 8, 10 };
dict[90] = new List() { 8, 3, 92, 138 };


Задача - найти кратчайший путь между словарями dict[1] и dict[90] по их связям. То
есть сначала сравнить все элементы в списке словаря с ключом 1 с 90. 
Потом сравнить по-очереди каждый элемент списка в словаре с ключом '87' с 90, потом
каждый элемент списка в словаре с ключом '6' и так далее, пока не будет достигнуто
совпадение с 90.
Полученный путь нужно запомнить в отдельный List, в котором будут ключи словарей в пути.

Пример:

dict[1] = new List(){ 3, 5 };
dict[3] = new List(){ 1, 8 };
dict[5] = new List(){ 1 };
dict[8] = new List(){ 3, 90 };
dict[90] = new List() { 8 };


Нужно найти кратчайшее расстояние от dict[1] до dict[90]. Сначала сравниваем все
элементы в dict[1] с 90. Элемента 90 в списке словаря '1' нет. Ищем дальше: берём первый
элемент списка словаря с ключом '1' (3) и сравниваем элементы словаря dict[3] с 90.
Элемента 90 в списке словаря '3' нет. Берём следующий элемент списка словаря '1' (5).
Там сравниваем всего один элемент и тоже нет совпадений. 

Теперь углубляемся и рассматриваем элементы словаря '3' как отдельные словари. Т.е.
проверяем dict[1] - ничего нет. Проверяем dict[8] - совпадение. Поиск закончен!

При этом нужно запоминать путь. В качестве вывода использовать

List output = new List();


В который по порядку будут записываться ключи словарей в пути. В примере он будет
состоять из элементов:

output.Add(1);
output.Add(3);
output.Add(8);
output.Add(90);


Очевидно, нужно использовать рекурсию для прохождения по всем элементам списков.
    


Ответы

Ответ 1



Рекурсию можно использовать, но не нужно. Задача сводится к обходу графа в ширину. Где словари - это вершины графа. А ключи доступа - ребра. Замечу, что это орграф. Извиняйте за введение собственной системы ввода данных, писал, чтобы проверить работоспособность. А раз написал, не грех и поделиться. На вход поступает 2 числа, N - кол-во ключей и M - кол-во записей о этих ключах. В следующих M строках описываются ключи в формате: 1-e число - ключ, последующие через пробел - ключи других словарей. Далее программа требует два числа X и Y. От какого словаря требуется найти путь к другому. Программа выводит кратчайший путь, если таковой существует, иначе "Don't exist". string[] Inp = Console.ReadLine().Split(); // Заносим данные ==>> int N = Convert.ToInt32(Inp[0]), M = Convert.ToInt32(Inp[1]); Dictionary> dict = new Dictionary>(); for (int i = 0; i < N; i++) dict[i] = new List(); for (int i = 0; i < M; i++) { Inp = Console.ReadLine().Split(); int Temp = Convert.ToInt32(Inp[0]) - 1; for (int j = 1; j < Inp.Length; j++) { dict[Temp].Add(Convert.ToInt32(Inp[j]) - 1); } } Inp = Console.ReadLine().Split(); int X = Convert.ToInt32(Inp[0]) - 1, Y = Convert.ToInt32(Inp[1]) - 1; // <<== Все еще заносим Queue Work = new Queue(); // BFS работает через очередь. bool[] Mark = new bool[N]; // Массив, в котором будем помечать посещенные словари int[] Log = new int[N]; // Потребуется для вывода найденного пути bool Exist = false; // Существует ли наш путь вообще Work.Enqueue(X); // см. Алгоритм BFS (обход в глубину) Mark[X] = true; Log[X] = -1; while (Work.Count > 0) { int v = Work.Dequeue(); for (int i = 0; i < dict[v].Count; i++) { if (!Mark[dict[v][i]]) { if (dict[v][i] == Y) Exist = true; Work.Enqueue(dict[v][i]); Mark[dict[v][i]] = true; Log[dict[v][i]] = v; } } } if (Exist) { List output = new List(); // Восстанавливаем путь for (int v = Y; v != -1; v = Log[v]) output.Add(v); output.Reverse(); for (int i = 0; i < output.Count; i++) Console.Write(output[i] + 1 + " "); Console.WriteLine(); } else Console.WriteLine("Don't exist"); Вот пример работы: Входные данные: 100 6 1 2 3 4 5 5 2 3 1 45 42 13 54 42 13 3 45 42 54 1 13 Выходные данные: 1 3 45 13 Надеюсь, помог.

среда, 4 марта 2020 г.

Как добавить элемент в начало списка Java?

#java #списки


Пробовал с имя_списка.add() и имя_списка.set(). Результат нулевой. 

    BufferedReader r = new BufferedReader(new InputStreamReader(System.in));
    ArrayList list = new ArrayList<>();
    for (int i = 0; i < 10; i++)
    {
        list.add(i, r.readLine());
    }
    for (int i = 0; i < 10; i++)
    {
        System.out.println(list.get(i));
    }

    


Ответы

Ответ 1



имя_списка.add(0,элемент_списка); UPDATE: BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); ArrayList list = new ArrayList<>(); for (int i = 0; i < 10; i++){ list.add(0, r.readLine()); } for (String str : list){ System.out.println(str); } Так как появились подробности, дополню: если в задаче нет чёткой привязки на интерфейс List, то правильнее использовать класс Stack c его LIFO. BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); Stack stack = new Stack<>(); for (int i = 0; i < 10; i++){ stack.push(r.readLine()); } while (!stack.isEmpty()){ System.out.println(stack.pop()); }

пятница, 31 января 2020 г.

Из одного списка сделать два вложенных друг в друга. Python

#python #списки


Есть список в формате:

list = [["f1", "object1"],["f1", "object2"],["f2", "object3"],["f2", "object4"],]


Пытаюсь сделать следующим образом:

ll = []    
for g in groupby( sorted(list,key=lambda x:x[1]) ,key=lambda x:x[1]):
    ll.append(g[0])

    for i in g[1]:
        ll.append(i[0])


Но на выходе получаю не совсем то, что нужно. Получаю одномерный список следующего
формата:

list3 = ["f1", "object1", "object2", "f2", "object3", "object4"]


Мне необходимо его сгруппировать так, чтобы на выходе получался следующий формат

listGroup = [["f1", ["object1", "object2"]], ["f2", ["object3", "object4"]]]

    


Ответы

Ответ 1



in_l = [["f1", "object1"], ["f1", "object2"], ["f2", "object3"], ["f2", "object4"],] from collections import defaultdict temp_dict = defaultdict(list) for fn, objn in in_l: temp_dict[fn].append(objn) out_l = [list(item) for item in temp_dict.items()] print(out_l) # [['f1', ['object1', 'object2']], ['f2', ['object3', 'object4']]]

Ответ 2



Если через groupby, то: items = [["f1", "object1"], ["f1", "object2"], ["f2", "object3"], ["f2", "object4"]] # В одну строку: new_items = [[key, [x[1] for x in item_list]] for key, item_list in groupby(items, key=lambda x: x[0])] print(new_items) new_items = [] for key, item_list in groupby(items, key=lambda x: x[0]): new_items.append([key, [x[1] for x in item_list]]) print(new_items) Иначе можно сгруппировать элементы вручную через словарь: items = [["f1", "object1"], ["f1", "object2"], ["f2", "object3"], ["f2", "object4"]] from collections import OrderedDict key_by_item_list = OrderedDict() for key, value in items: if key not in key_by_item_list: key_by_item_list[key] = [] key_by_item_list[key].append(value) print(key_by_item_list) # OrderedDict([('f1', ['object1', 'object2']), ('f2', ['object3', 'object4'])]) # В одну строку: new_items = [[key, item_list] for key, item_list in key_by_item_list.items()] print(new_items) new_items = [] for key, item_list in key_by_item_list.items(): new_items.append([key, item_list]) print(new_items)

Ответ 3



Решение в стиле Pandas: import pandas as pd In [153]: df = pd.DataFrame(lst, columns=['c1','c2']) In [155]: df.groupby('c1')['c2'].apply(list).reset_index().values.tolist() Out[155]: [['f1', ['object1', 'object2']], ['f2', ['object3', 'object4']]] Пошагово: In [161]: df Out[161]: c1 c2 0 f1 object1 1 f1 object2 2 f2 object3 3 f2 object4 In [162]: df.groupby('c1')['c2'].apply(list) Out[162]: c1 f1 [object1, object2] f2 [object3, object4] Name: c2, dtype: object In [163]: df.groupby('c1')['c2'].apply(list).reset_index() Out[163]: c1 c2 0 f1 [object1, object2] 1 f2 [object3, object4] In [164]: df.groupby('c1')['c2'].apply(list).reset_index().values.tolist() Out[164]: [['f1', ['object1', 'object2']], ['f2', ['object3', 'object4']]]

среда, 29 января 2020 г.

Как перемножить списки в Python 3.x

#python #python_3x #списки


Имеется текстовый файл с данным содержанием "D11i14V17A8n7R11H16r18P18E5h18i18x8O16Y5s13d1X6u4a20C2p2O14J5I11L5K6y4X19n10s17h1E17Q5Z1U1K6U8E8L11n14k6".
Я написал код который разделяет буквенные значения в один список, цифровые в другой.
Как правильно перемножить данные из списка, чтобы в конечном итоге получилось D * 11
= DDDDDDDDDDD (Буква D 11 раз). Код ниже:

import re
c = []
d = []
result = 0
with open('dest.txt', 'r') as s:
    for line in s:
        line = line.strip()
        lst = re.split('(\d*)', line)

        for i in lst:
            if i.isalpha() == True:
                c.append(i)
            else:
                d.append(i)

for a, b in zip(c, d): 
     result += a*b

print(result)


Результат:

D:\Python\lib\re.py:212: FutureWarning: split() requires a non-empty pattern match.
  return _compile(pattern, flags).split(string, maxsplit)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
 in ()
     16 
     17 for a, b in zip(c, d):
---> 18      result += a*b
     19 
     20 print(result)

TypeError: can't multiply sequence by non-int of type 'str'

    


Ответы

Ответ 1



In [20]: import re In [21]: task = "D11i14V17A8n7R11H16r18P18E5h18i18x8O16Y5s13d1X6u4a20C2p2O14J5I11L5K6y4X19n10s17h1E17Q5Z1U1K6 ...: U8E8L11n14k6" In [22]: numbers, lit = list(re.findall(r'\d+', task)), list(re.findall(r'\D', task)) In [23]: ''.join(int(x) * y for x, y in zip(numbers, lit)) Результат: Out[25]: 'DDDDDDDDDDDiiiiiiiiiiiiiiVVVVVVVVVVVVVVVVVAAAAAAAAnnnnnnnRRRRRRRRRRRHHHHHHHHHHHHHHHHrrrrrrrrrrrrrrrrrrPPPPPPPPPPPPPPPPPPEEEEEhhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiiiiiixxxxxxxxOOOOOOOOOOOOOOOOYYYYYsssssssssssssdXXXXXXuuuuaaaaaaaaaaaaaaaaaaaaCCppOOOOOOOOOOOOOOJJJJJIIIIIIIIIIILLLLLKKKKKKyyyyXXXXXXXXXXXXXXXXXXXnnnnnnnnnnssssssssssssssssshEEEEEEEEEEEEEEEEEQQQQQZUKKKKKKUUUUUUUUEEEEEEEELLLLLLLLLLLnnnnnnnnnnnnnnkkkkkk'

Ответ 2



Чтобы распаковать файл/обратить кодирование длин серий (RLE), удобно использовать re.split(), чтобы отделить символы от цифр, и zip(), чтобы попарно их обойти: import re from pathlib import Path rle_tokens = iter(re.split(r'(\d+)', Path('rle.txt').read_text())) decompressed = ''.join([char * int(digits) for char, digits in zip(rle_tokens, rle_tokens)]) print(decompressed) Объяснение как zip(it, it) работает. Результат: DDDDDDDDDDDiiiiiiiiiiiiiiVVVVVVVVVVVVVVVVVAAAAAAAA...

Ответ 3



Вот так переделал код. Надо было присвоить result пустую строку. import re c = [] d = [] result = '' with open('dest.txt', 'r') as s: for line in s: line = line.strip() lst = re.split('(\d+)', line) for i in lst: if i.isalpha() == True: c.append(i) else: d.append(i) for a, b in zip(c, d): str(a) * int(b) result += str(a) * int(b) print(result)

вторник, 28 января 2020 г.

Можно ли улучшить поиск в многомерном списке python

#python #python_3x #поиск #списки


Здравствуйте. Есть многомерный список:

spisok=[{'a':'1','b':'2'},{'a':'3','b':'4'}]


необходимо проверить есть ли в списке ключ 'a' с определенным значением.
поискав в интернете смог собрать такую конструкцию:

for i in range(len(spisok)):
    if spisok[i]['a'] == stroka_poiska:
        print('True')


Можно ли как то улучшить данный код? И верна ли логика построения алгоритма?
    


Ответы

Ответ 1



Попробуйте так: def chk_for_val(lst, key, val): for d in lst: if d.get(key) == val: return True return False При использовании dict.get(key) (вместо dict[key]) - не будут генерироваться исключения для несуществующих ключей In [138]: chk_for_val(lst, 'a', '3') Out[138]: True In [139]: chk_for_val(lst, 'X', '3') Out[139]: False

Ответ 2



Самый лучший вариант: check = any(dct.get('a')==stroka_poiska for dct in spisok) print(check) Лаконично, читабельно и эффективно.

Ответ 3



Предложу свой немного странноватый вариант: In [17]: array = [{'a':'1','b':'2'},{'a':'3','b':'4'}] In [18]: def search(dictionary, key, value): ...: verbose_data = sum(map(list, map(dict.items, dictionary)), []) ...: return (key, value) in verbose_data ...: In [19]: search(array, 'a', '1') Out[19]: True In [20]: search(array, 'a', '4') Out[20]: False Или: In [27]: search = lambda array, key, value: any(map(lambda x: (key, value) in x.items(), array)) In [28]: search(array, 'a', '1') Out[28]: True In [29]: search(array, 'A', '1') Out[29]: False Или: In [13]: from itertools import takewhile In [14]: def search(array, key, value): ...: return len(list(takewhile(lambda x: x.get(key) != value, array))) < len(array) ...: In [15]: search(array, 'a', '3') Out[15]: True In [16]: search(array, 'A', '3') Out[16]: False

Ответ 4



В стандартной библиотеке collections есть класс отображения ChainMap. Он хранит список отображений, так что их можно просматривать как единое целое. Поиск производится в каждом отображении по порядку и завершается успешно, если ключ найден хотя бы в одном. from collections import ChainMap spisok=[{'a':'1','b':'2'},{'a':'3','b':'4'}] chain = ChainMap(*spisok) print(chain.get('a')) документация

Ответ 5



Я вот так сделал: for i in range(len(spisok)): for key in spisok[i]: if spisok[i][key]=='4': print(spisok[i][key])

воскресенье, 26 января 2020 г.

Как преобразовать список списков в список чисел (Python)

#python #python_3x #списки


Имеется:

[[1], [2], [3]]


нужно:

[1, 2, 3]

    


Ответы

Ответ 1



>>> a = [[1], [2], [3]] >>> sum(a, []) [1, 2, 3] >>> a = [[1, 2], [3, 4], [5, 6]] >>> sum(a, []) [1, 2, 3, 4, 5, 6]

Ответ 2



>>> a = [[1], [2], [3]] >>> [x for l in a for x in l] [1, 2, 3]

Ответ 3



Ну или вот так: from itertools import chain list(chain.from_iterable(a))

Ответ 4



a = [[1], [2], [3]] print([i[0] for i in a])

Ответ 5



result = [] for x in [[1], [2], [3]]: result.append(x.pop()) или [x.pop() for x in [[1], [2], [3]]]

Ответ 6



Вариант через from functools import reduce: from functools import reduce from operator import add items = [[1], [2], [3]] print(reduce(add, items)) # [1, 2, 3] # Через лябмду print(reduce(lambda a, b: a + b, items)) # [1, 2, 3]

Ответ 7



еще вариант outer = [[1,2,3], [4,5,6], [7,8,9]] new_list = [item for sublist in outer for item in sublist] получим [1,2,3,4,5,6,7,8,9]

четверг, 23 января 2020 г.

Циклический список для представления людей

#cpp #указатели #списки


Только начал разбираться со списками. Один момент в упор не понимаю. В книге Седжвика
"Алгоритмы на С++" есть такой пример создания циклического списка для представления
людей, расставленных в круг:

struct node {
    int item;
    node *next;

    node(int x, node *t){
        item = x;
        next = t;
    }
};

int main() {
    typedef node *link;
    link t = new node(1, 0);
    t->next = t;    
    link x = t;

    for (int i = 2; i < 5; i++)
        x = (x->next = new node(i, t));

    while (x != x->next){
        for (int i = 1; i < 5; i++)
            x = x->next;
        x->next = x->next->next;
    }

    cout << x->item << endl;

    delete t;   
    return 0;
}


Что делает этот блок:

link t = new node(1, 0);
t->next = t;
link x = t;


1 строка: создается указатель на экземпляр класса node, инициализируются его поля
и выделяется память;

2 строка: аналогично (*t).next = t; В классе node есть поле next - указатель на node,
которому присваивается t - указатель на экземпляра класса node? Можете объяснить, зачем
это делается?

3 строка: аналогична строке link x = new node(1, 0); - первой строке?

P.S. И что означает x->next = x->next->next;? Следующему элементу в списке присваивается
значение, хранящееся после следующего элемента списка:

(*x).next = (*(*x).next)).next;
    


Ответы

Ответ 1



Как вы сами написали, создается кольцевой список. А это означает, что последний элемент списка указывает на первый элемент списка. Так сказать, все встали в круг и взялись за руки.:) В этих предложениях link t = new node(1, 0); t->next = t; создается первый элемент списка link t = new node(1, 0); который указывает сам на себя t->next = t; Затем в цикле создаются еще три элемента link x = t; for (int i = 2; i < 5; i++) x = (x->next = new node(i, t)); Каждый раз при добавлении нового элемента его поле next указывает на первый элемент new node(i, t) Указатель этого созданного элемента присваивается полю next предыдущего элемента, а переменная x получает значение созданного указателя. Можно вставить отладочную печать #include struct node { int item; node *next; node(int x, node *t){ item = x; next = t; } }; int main() { typedef node *link; link t = new node( 1, 0 ); t->next = t; link x = t; for ( int i = 2; i < 5; i++ ) { x = ( x->next = new node( i, t) ); } do { x = x->next; std::cout << "current: " << ( void * )x << ", value: " << x->item << ", next: " << ( void * )x->next << std::endl; } while ( x->next != t ); return 0; } И получить примерно следующий вывод на консоль current: 0x9cc570, value: 1, next: 0x9cc590 current: 0x9cc590, value: 2, next: 0x9cbda0 current: 0x9cbda0, value: 3, next: 0x9cbdc0 current: 0x9cbdc0, value: 4, next: 0x9cc570 Как видно, последний элемент в списке указывает на первый элемент в списке, который имеет адрес 0x9cc570 В этой тестовой программе цикл do-while начинается с предложения x = x->next; потому что перед этим в другом цикле при создании элементов x - это указатель последнего созданного элемента и его поле next указывает на первый элемент списка. Потому данное предложение при первой итерации цикла как раз и переходит на первый элемент списка. Экспериментируйте с программой из книги и смотрите, что получается!

Ответ 2



Если записать этот код нормально, то будет node* t = new node(1, nullptr); // создаем новый элемент с типом node, // в его конструктор передаем 1 (поле item) и нулевой указатель (поле next) t->next = t; // делаем так чтобы поле next указывало на самого себя Мы не можем написать node* t = new node(1, t);, потому что в момент вызова конструктора переменная t еще не заполнена.

понедельник, 6 января 2020 г.

Присвоение класса вложенным и предшествующим элементам

#javascript #jquery #списки


Здравствуйте. Есть следующая разметка html



$('.cd-main-nav li').has('ul').addClass('test');
.test {
background-color:red;
}






Вопрос: как присвоить новый класс (допустим .test как в примере) тегу A, который
находится в LI, имеющему вложенный список UL, и предшествует этому UL.
Сейчас класс присваивается всему пункту LI.
Сколько не бился - не выходит. Помогите справиться с этой задачкой. 
    


Ответы

Ответ 1



Для подсветки всех вложенных элементов: поиск элементов внутри выбранных - метод .find() $('.cd-main-nav li').has('ul').find('a').addClass('test'); Для подсветки родителей этих вложенных элементов: поиск элементов лежащих перед выбранными $('.cd-main-nav li ul').prev().addClass('test');

Ответ 2



$('.cd-main-nav li').has('ul').find('a').addClass('test'); .test { background-color:red; } Если же надо выделать только ссылки в внутреннем списке тогда - $('.cd-main-nav li ul a').addClass('test'); .test { background-color:red; }

Ответ 3



$('.cd-main-nav li').has('ul') .children('a') .addClass('test');

вторник, 31 декабря 2019 г.

Последовательное чтение значений из списка в словарь

#python #списки #словари


Здраствуйте. Есть готовый список, который содержит слова в определенной последовательности:
list = ['To', 'be', 'or', 'not', 'to', 'be']

Требуется подсчитать частоту появления слов в этом списке и составить словарь, который
содержит пары значений (слово) : (кол-во повторений этого слова).
Вот мой код:
# -*- coding: utf-8 -*-

list = ['To', 'be', 'or', 'not', 'to', 'be']

counts = dict()
for word in list:
    if word not in counts: # Если символа нет в словаре, создаем новую запись
        counts[word] = 1
    else: # В противном случае инкрементируем d[c]
        counts[word] += 1

print counts

Подсчет слов ведется правильно. 
Проблема в том, что моя программа помещает новые записи в словарь в хаотичном порядке,
а не так, как слова шли в предложении. Вот что интерпретатор выдает на выходе:
{'not': 1, 'To': 1, 'or': 1, 'to': 1, 'be': 2}

А должно быть так:
{'To': 1, 'be': 2, 'or': 1, 'not': 1, 'to': 1}

Как можно получить такой результат? Никак не могу монять.
P.S.: В Python я новичок, поэтому не судите строго.    


Ответы

Ответ 1



Вам нужен OrderedDict, Dict не поддерживает упорядочивание записей по умолчанию.

Ответ 2



Словарь в Питоне не обязан сохранять порядок добавления слов. Хотя некоторые реализации Питона, такие как Pypy, используют упорядоченные словари по умолчанию. Чтобы подсчитать частоту появления слов во входном списке и напечатать их в том порядке как они заданы в списке, можно объединить Counter и OrderedDict, чтобы поддерживать порядок вставки: #!/usr/bin/env python from collections import Counter, OrderedDict class OrderedCounter(Counter, OrderedDict): pass input_list = ['To', 'be', 'or', 'not', 'to', 'be'] frequencies = OrderedCounter(input_list) for item, count in frequencies.items(): print("{}\t{}".format(item, count)) Результат To 1 be 2 or 1 not 1 to 1 Если достаточно только напечатать результат, то можно обойтись только Counter, без OrderedDict или сортировки: #!/usr/bin/env python from collections import Counter input_list = ['To', 'be', 'or', 'not', 'to', 'be'] frequencies = Counter(input_list) for item in input_list: count = frequencies.pop(item, None) if count is None: continue print("{}\t{}".format(item, count)) Результат совпадает с предыдущим кодом. frequencies словарь разрушается во время печати.

Ответ 3



Вот один вариант как отсортировать: ссылка

Ответ 4



Если вам нужно просто отсортировать в алфавитном порядке, просто при выводе сделайте for key in sorted(counts.keys()): print '{0}: {1}'.format(key, counts[key]) А если именно в том порядке, в котором они встречались в первый раз - да, как писал Etki, используйте OrderedDict

воскресенье, 29 декабря 2019 г.

Как поменять значения в списке, не создавая новый список?

#python #python_3x #списки


Стандартный способ через списковое включение (list comprehension):

my_list=[1,2,3,4,5,6]
my_list_1=[i*5 for i in my_list]
print(my_list_1)


Почему не могу сделать также с исходным списком:

my_list=[1,2,3,4,5,6]
for i in my_list:
    i*=5
print(my_list)


Этот код не работает, почему?
    


Ответы

Ответ 1



Для данной конструкции: for i in my_list: i*=5 Python создает новую переменную i в которую попадают последовательно значения из списка. В итоге вы меняете значения переменной i, список остается неизменным. In [136]: my_list=[1,2,3,4,5,6] ...: for i in my_list: ...: i*=5 ...: print(i, my_list) ...: 5 [1, 2, 3, 4, 5, 6] 10 [1, 2, 3, 4, 5, 6] 15 [1, 2, 3, 4, 5, 6] 20 [1, 2, 3, 4, 5, 6] 25 [1, 2, 3, 4, 5, 6] 30 [1, 2, 3, 4, 5, 6] В следующей строке кода вы присваиваете переменной my_list_1 результат работы list comprehension (список): my_list_1=[i*5 for i in my_list] Решение: my_list[:] = [i*5 for i in my_list] print(my_list) #[5, 10, 15, 20, 25, 30] PS подробнее о присвоениях спискам и срезах можно прочитать в ответах на этот вопрос в англоязычной версии SO

Ответ 2



Вы можете менять значение элемента списка, но вам нужно знать его индекс, для этого используются следующие способы: Через range: my_list = [1, 2, 3, 4, 5, 6] for i in range(len(my_list)): my_list[i] = my_list[i] * 5 print(my_list) # [5, 10, 15, 20, 25, 30] Через enumerate: my_list = [1, 2, 3, 4, 5, 6] for i, x in enumerate(my_list): my_list[i] = x * 5 print(my_list) # [5, 10, 15, 20, 25, 30] PS. А код с списковым включением: my_list=[1,2,3,4,5,6] my_list_1=[i*5 for i in my_list] print(my_list_1) # [5, 10, 15, 20, 25, 30] тоже самое что: my_list=[1,2,3,4,5,6] my_list_1=[] for i in my_list: my_list_1.append(i * 5) print(my_list_1) # [5, 10, 15, 20, 25, 30]

пятница, 27 декабря 2019 г.

Поэлементное сложение списков различной длины

#python #python_3x #списки


Есть два списка: 

a = [1,2,3]
b = [4,5,6,7,8]


Нужно получить такой результат:

a + b = [5,7,9,7,8]


Надеюсь, смысл понятен.
Просто в голове одни костыли, хотелось бы найти более менее адекватное решение.
    


Ответы

Ответ 1



Ещё несколько вариантов решения: longer = a if len(a) >= len(b) else b c = [x+y for x,y in zip(a,b)] + longer[min(len(a), len(b)):] или одной командой (менее читабельный вариант): c = [x+y for x,y in zip(a,b)] + (a if len(a) >= len(b) else b)[min(len(a), len(b)):] замеры времени выполнения для массивов длиной около 10^6: In [110]: print(len(a), len(b)) 1000000 1012345 In [104]: %timeit Sergey_Gornostaev(a, b) 267 ms ± 318 µs per loop (mean ± std. dev. of 7 runs, 1 loop each) In [105]: %timeit nick_gabpe(a, b) 200 ms ± 168 µs per loop (mean ± std. dev. of 7 runs, 1 loop each) In [106]: %timeit AtachiShadow1(a, b) 255 ms ± 558 µs per loop (mean ± std. dev. of 7 runs, 1 loop each) In [107]: %timeit AtachiShadow2(a, b) 239 ms ± 1.69 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) In [108]: %timeit AtachiShadow3(a, b) 200 ms ± 604 µs per loop (mean ± std. dev. of 7 runs, 1 loop each) In [109]: %timeit MaxU(a, b) 124 ms ± 1.26 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) setup: from random import randint a = [randint(0, 10**6) for _ in range(10**6)] b = [randint(0, 10**6) for _ in range(10**6+12345)] print(len(a), len(b)) # 1000000 1012345 def MaxU(a, b): return [x+y for x,y in zip(a,b)] + (a if len(a) >= len(b) else b)[min(len(a), len(b)):] def nick_gabpe(a, b): c=[] for x, y in zip(a,b): c+=[x+y] if len(a) > len(b): c+=a[len(a)-len(b)+1:] elif len(a) < len(b): c+=b[len(b)-len(a)+1:] return c def Sergey_Gornostaev(a, b): return [sum(i) for i in zip_longest(a, b, fillvalue=0)] def AtachiShadow1(a, b): c = [] la = len(a) lb = len(b) maxab = max(la, lb) minab = min(la, lb) for i in range(maxab): if i >= minab: if la < lb: c.append(0+b[i]) elif la > lb: c.append(a[i]+0) else: c.append(a[i]+b[i]) return c def AtachiShadow2(a, b): c = [] la = len(a) lb = len(b) maxab = max(la, lb) minab = min(la, lb) for i in range(maxab): try: c.append(a[i]+b[i]) except IndexError: if la < lb: c.append(0+b[i]) elif la > lb: c.append(a[i]+0) return c def AtachiShadow3(a, b): c=[] la = len(a) lb = len(b) for x, y in zip(a,b): c+=[x+y] if la > lb: c+=a[la-lb+1:] elif la < lb: c+=b[lb-la+1:] return c

Ответ 2



from itertools import zip_longest c = [sum(i) for i in zip_longest(a, b, fillvalue=0)]

Ответ 3



Спасибо огромное автору за достаточно клёвую задачку, мне (как новичку) было очень интересно разобраться как это работает) сообщение длинное, но надеюсь лёгкое в прочтении))): Предложенный в комментарии zip и в ответе zip_longest я решил оставить на последние варианты, а до них попробовать самостоятельно решить данный вопрос. И вот что я делал. Первый вариант был таким: a = [1, 2, 3] b = [4, 5, 6, 7, 8] c = [] for i in range(max(len(a), len(b))): if i >= min(len(a), len(b)): if len(a) == i + 1 and len(a) < len(b): c.append(0 + b[i]) else: c.append(a[i] + 0) else: c.append(a[i] + b[i]) print(c) Он, конечно же делает то, что должен. Но мне пришла вторая мысль о более логическом (как мне казалось) решении этого цикла. Второй вариант был таким: a = [1, 2, 3] b = [4, 5, 6, 7, 8] c = [] for i in range(max(len(a), len(b))): try: c.append(a[i] + b[i]) except IndexError: if i >= min(len(a), len(b)): if len(a) == i + 1 and len(a) < len(b): c.append(0 + bbb[i]) else: c.append(a[i] + 0) print(c) С одной стороны, мне казалось, что до тех пор, пока меньший список не перестанет итерироваться, будет выполняться тело try:. А когда меньший список закончится, возникнет except IndexError и исполнится тело ошибки. Так как и этот вариант тоже делает то, что задумано, логично было бы сравнить их по скорости выполнения. Я использовал timeit и получил следующие результаты: Вариант №1 - 8.045603216 на 1000000 (1млн) повторений Вариант №2 - 9.076056240 на 1000000 (1млн) повторений Я удивился. Я надеялся, что try: исключит лишнее выполнение if внутри цикла и тем самым ускорится. Но оказалось наоборот. После этого, я решил сравнить свои варианты с вариантом Sergey Gornostaev, и timeit второй раз указал на неэффективность моих методов: Вариант №1 - 8.045603216 на 1000000 (1млн) повторений Вариант №2 - 9.076056240 на 1000000 (1млн) повторений Sergey Gornostaev - 6.960991073 на 1000000 (1млн) повторений Идея того, что на стэковерфлоу чаще всего предлагают "опытные" ответы в очередной раз подтвердилась)))) 1 секунда выигрыша у Sergey Gornostaev)) Но я подумал, стандартная библиотека == оптимизированный код. Я пошёл оптимизить свой код - в моих вариантах функции len(), max(), min() запускаются в каждой итерации цикла, а зачем мне 5 раз len(a) просить?)))) я тоже подумал что это немного тупо и надо исправить)))) вынес их в переменные до цикла и подправил логику if. Вариант №1.1: a = [1, 2, 3] b = [4, 5, 6, 7, 8] c = [] la = len(a) lb = len(b) maxab = max(la, lb) minab = min(la, lb) for i in range(maxab): if i >= minab: if la < lb: c.append(0+b[i]) elif la > lb: c.append(a[i]+0) else: c.append(a[i]+b[i]) print(c) Вариант №2.1: a = [1, 2, 3] b = [4, 5, 6, 7, 8] c = [] la = len(a) lb = len(b) maxab = max(la, lb) minab = min(la, lb) for i in range(maxab): try: c.append(a[i]+b[i]) except IndexError: if la < lb: c.append(0+b[i]) elif la > lb: c.append(a[i]+0) print(c) И да, оптимизация сработала: Вариант №1.1 - 4.774997384 на 1000000 (1млн) повторений Вариант №2.1 - 5.907061396 на 1000000 (1млн) повторений Sergey Gornostaev - 6.960991073 на 1000000 (1млн) повторений Почти двукратный прирост к скорости, за счёт правильного написания кода. И оба варианта быстрее предложенной библиотеки))) И когда я уже было хотел внести очередную правку в свой ответ, появился ответ автора: a = [1,2,3] b = [4,5,6,7,8] c=[] for x, y in zip(a,b): c+=[x+y] if len(a) > len(b): c+=a[len(a)-len(b)+1:] elif len(a) < len(b): c+=b[len(b)-len(a)+1:] print(c) И что же timeit? Разнёс в щепки, все предыдущие варианты))): Вариант №1.1 - 4.774997384 на 1000000 (1млн) повторений Вариант №2.1 - 5.907061396 на 1000000 (1млн) повторений Sergey Gornostaev - 6.960991073 на 1000000 (1млн) повторений Автор - 3.151485595 на 1000000 (1млн) повторений Выигрыш 1,5 секунды перед моим "быстым" оптимизированным вариантом. Идея хорошего ответа на СО второй раз за один вопрос подтвердила себя)))))) НО! что ещё более круто в ответе самого автора, это использование zip. В ответе Sergey Gornostaev и в моих вариантах используется концепция большего списка, то есть, for будет делать столько циклов сколько элементов в большем списке - это есть ПЛОХО. Наводящий вопрос - что будет, если один из списков будет пустым? А вот что: a = [] Вариант №1.1 - 4.960243867 на 1000000 (1млн) повторений Вариант №2.1 - 7.668313717 на 1000000 (1млн) повторений Sergey Gornostaev - 6.865756772 на 1000000 (1млн) повторений Автор - 1.848900008 на 1000000 (1млн) повторений Вот это поворот!!! Вариант №1.1 остался таким же (0.2 на погрешность). А вот Вариант №2.1 (try except) просто провалился - из-за того что a = [], каждая итерация for теперь приводит к except, что и увеличило время выполнения. Вариант Sergey Gornostaev тоже,как и 1.1 не изменился во времени исполнения (0.1 на погрешность). Но вот ответ автора показал себя в полной красе, это по круче любой "чашки сахара" (из мультика про "вот это поворот"))))) почти 3-х кратное превосходство на самым быстрым вариантом "длинного списка"! Но это ещё не всё!))) Я же помнил стандартная библиотека == оптимизированный код, а код автора немножко не оптимизирован)))) функции len(a) и len(b) вызываются дважды за выполнение. Значит их можно посчитать один раз в начале и потом пользоваться переменной)): a = [1,2,3] b = [4,5,6,7,8] c=[] la = len(a) lb = len(b) for x, y in zip(a,b): c+=[x+y] if la > lb: c+=a[la-lb+1:] elif la < lb: c+=b[lb-la+1:] И вот что говорит timeit: a = [1,2,3]: не оптимизированный код автора - 3.138068215 оптимизированный код автора - 2.908475165 a = []: не оптимизированный код автора - 1.909198691 оптимизированный код автора - 1.642370166 Выигрыш на уровне 0.25 секунды у оптимизированного кода. И это уже точное 3-х кратное превосходство над кодом "длинного списка"! Это круто)))) P.S. 1. Все измерения проводились на одном компьютере, и в приблизительно одинаковых условиях (отличия мог вносить только открытый рядом Chrome), из этого следует что отношение времени в выполнении разных логик будут другими на другом компьютере, а здесь показывается лишь приблизительная ОТНОСИТЕЛЬНАЯ скорость выполнения логик. 2. Я не профессионал, даже не любитель, timeit я гуглил во время написания ответа. Пока что ко мне больше применимо понятие "Script kiddie", но я учусь))))) Именно поэтому, я предполагаю, что мои исследования в этом вопрос-ответе будут содержать ошибки и не точности. И буду рад если о них мне расскажут более опытные программисты)))). 3. Ещё раз БОЛЬШОЕ спасибо автору вопроса-задачи и тем кто отвечал и комментировал его, мне было приятно несколько раз удивляться своей несостоятельности, и так же было приятно учиться на этом)) 4. Приведённые измерения и отличия на 1 млн повторений могут вызвать закономерный вопрос о том, а стоит ли париться из-за таких мелких отличий, тем более что речь идёт о Python а не о C++ к примеру. И я считаю что каждый сам решит стоит ли оно того))))))) Как минимум для разминки это можно было сделать)

Ответ 4



Ну и напишем свой велосипед: a = [1,2,3] b = [4,5,6,7,8] c=[] for x, y in zip(a,b): c+=[x+y] if len(a) > len(b): c+=a[len(a)-len(b)+1:] elif len(a) < len(b): c+=b[len(b)-len(a)+1:] print(c) # [5, 7, 9, 6, 7, 8]

четверг, 19 декабря 2019 г.

Создать список чисел из строки с числами через пробел

#python #массивы #python_3x #строки #списки


Имеется строка:  


  '45 30 55 20 80 20'.


Необходимо создать массив из чисел входящих в данную строку:  


  [45, 30, 55, 20, 80, 20].


Долго думал и пришел только к такому коду, только не работает:

for i in range(len(q)):
    if q[i]!=(' '):
        e+=q[i]
    else:
        w.append(int(e))
        e=''
 print(w)   


проблема в том что последнюю 20 он не видит, прошу помогите
    


Ответы

Ответ 1



Можно воспользоваться генератором списка: s = '45 30 55 20 80 20' a = [int(x) for x in s.split()]

Ответ 2



UPD. поправлено исходя из комментариев s = '45 30 55 20 80 20' print (list(map(int, s.split()))) [45, 30, 55, 20, 80, 20]

суббота, 14 декабря 2019 г.

Зачем в Python 3.3 ввели list.copy() если можно использовать слайс без указания границ?

#python #python_3x #списки


Сейчас плотно изучаю нюансы методов встроенных типов, и есть один вопрос, на который
не могу найти ответ.

В Python 3.3 ввели метод списка copy(), который делает поверхностную копию.
Но то же самое можно было делать, просто взяв срез без границ - list[:]

Зачем было вводить новый метод?

Подозреваю, что какая-то причина есть. Например, сначала я считал таким же ненужным
нововведением метод list.clear(), введённый в том же Python 3.3, ведь есть же list = []

А потом я прочитал, что эти две синтаксические конструкции по разному работают в
ситуациях, когда на список ссылаются две или более переменных.

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


Ответы

Ответ 1



Чтобы сделать операции del list_[:] и shallow_copy = list_[:] более очевидными (discoverable) и читаемыми для новичков (list_.clear() и shallow_copy = list_.copy() соответственно). See Add list.clear() and list.copy() В целом, в Питоне есть предпочтение к использованию слов вместо пунктуации, например: and, or вместо &&, || или on_true if cond else on_false вместо cond ? on_true : on_false или re.search(regex) вместо /regex/. Дополнительно, наличие явного метода list_.clear() может уменьшить вероятность возможной ошибки:list_ = [] существенно отличается от del list_[:] (первая конструкция создаёт новый список и привязывает его к имени list_, вторая конструкция удаляет все элементы из существующего объекта (изменяемой последовательности такой как список наиболее вероятно)). Недостаток, что эти методы дублируют функциональность изменяемых последовательностей/отображений (объекты с __getitem__, __delitem__ методами).

воскресенье, 8 декабря 2019 г.

Пересечение списков словарей по определенному значению ключа. Необходима оптимизация кода

#python #python_3x #python_2x #списки #словари


Есть два списка словарей:


types - содержит информацию о названиях спортивных секциях и датах их посещения одного
посетителя.
user_list - содержит информацию о людях которые тоже ходили в этот спортзал и даты
их посещения.

types = [{'type':'tango', 'date':'200118'}, 
{'type':'box', 'date':'110217'},
{'type':'strip', 'date':'170518'}]

user_list = [{'name':'maxim', 'date':'200118'},
{'name':'ivan', 'date':'110217'},
{'name':'elena', 'date':'051018'},
{'name':'gleb', 'date':'170518'},  
{'name':'magas', 'date':'110217'},  
{'name':'ivan', 'date':'170518'}, 
{'name':'maxim', 'date':'110217'}]



Задача

Необходимо из второго списка выбрать посетителей которые были в одни и те же даты
с тем, кто указан в первом списке. И ещё условие: что были они в те же даты более одного
раза. 

Результат

В итоге должен получится вот такой список:

[{'date': '110217', 'name': 'ivan'}, {'date': '170518', 'name': 'ivan'}, {'date':
'200118', 'name': 'maxim'}, {'date': '110217', 'name': ‘maxim'}]


Если по другому объяснить, то нужно из второго списка выбрать строки значения полей
‘дата’, которые присутствуют в первом списке и встречаются во втором более одного раза.
Ниже представлено мое решение, которое мне ужасно не нравится. Часть кода можно заменить
на генераторы, но может быть кто нибудь знает более оптимальные инструменты?

types = [ {'type':'tango', 'date':'200118'},
{'type':'box', 'date':'110217'},
{'type':'strip', 'date':'170518'}]

user_list = [{'name':'maxim', 'date':'200118'},
{'name':'ivan', 'date':'110217'},
{'name':'elena', 'date':'051018'},
{'name':'gleb', 'date':'170518'},
{'name':'magas', 'date':'110217'},
{'name':'ivan', 'date':'170518'},
{'name':'maxim', 'date':'110217'}]

dates = [dct['date'] for dct in types]
names = list(set([dct['name'] for dct in user_list]))


goodusers = []
for name in names:
    res = []
    for u in user_list:
        if u['name']==name and u['date'] in dates:
            res.append(u)
    if len(res)>1:
        goodusers += res

print(goodusers)

    


Ответы

Ответ 1



Вы начали верно. Только нет смысла извлекать отдельно имена. from itertools import groupby dates = [dct['date'] for dct in types] # фильтруем по совпадению дат users = (d for d in user_list if d['date'] in dates) # сортируем, для работы функции группировки key_name = lambda u: u['name'] users = sorted(users, key=key_name) double_users = [] for _, u in groupby(users, key=key_name): pool = tuple(u) if len(pool) > 1: double_users += pool print(double_users)

Ответ 2



Такие задачи удобно решать используя модуль Pandas: import pandas as pd # pip install pandas сначала создадим DataFrame's из списков словарей: u = pd.DataFrame(user_list) t = pd.DataFrame(types) получились след. DataFrame's: In [59]: u Out[59]: date name 0 200118 maxim 1 110217 ivan 2 051018 elena 3 170518 gleb 4 110217 magas 5 170518 ivan 6 110217 maxim In [60]: t Out[60]: date type 0 200118 tango 1 110217 box 2 170518 strip теперь их можно отфильтровать одной коммандой: res = u.merge(t[['date']]).sort_values('name').groupby('name').filter(lambda x: len(x) > 1) результат: In [64]: res Out[64]: date name 1 110217 ivan 5 170518 ivan 0 200118 maxim 3 110217 maxim тоже самое в виде списка словарей: In [67]: res.to_dict('records') Out[67]: [{'date': '110217', 'name': 'ivan'}, {'date': '170518', 'name': 'ivan'}, {'date': '200118', 'name': 'maxim'}, {'date': '110217', 'name': 'maxim'}]

Ответ 3



Ну тогда вот, благодаря автору вопроса и его гостю: import sqlite3 as li3 conn = li3.connect(":memory:") cursor = conn.cursor() cursor.execute("CREATE TABLE visits (name TEXT, date TEXT)") cursor.execute("CREATE TABLE types ( type TEXT, date TEXT)") cursor.executemany("INSERT INTO visits VALUES (?,?)", [list(d.values()) for d in user_list]) cursor.executemany("INSERT INTO types VALUES (?,?)", [list(d.values()) for d in types]) conn.commit() cursor.execute(""" SELECT * FROM visits WHERE name IN (SELECT visits.name FROM visits INNER JOIN types ON visits.date=types.date GROUP BY visits.name HAVING count(visits.name) >1) """) print(cursor.fetchall()) cursor.close() Выдает: [('maxim', '200118'), ('ivan', '110217'), ('ivan', '170518'), ('maxim', '110217')]

вторник, 26 ноября 2019 г.

Как найти все дублирующиеся элементы в списке и количество их повторов?


Нужна функция, которая, например, для списка [10, 10, 23, 10, 123, 66, 78, 123] вернёт {10: 3, 123: 2}.
    


Ответы

Ответ 1



Реализация Первый способ: A = [10, 10, 23, 10, 123, 66, 78, 123] counter = {} for elem in A: counter[elem] = counter.get(elem, 0) + 1 doubles = {element: count for element, count in counter.items() if count > 1} print(doubles) Второй способ: from collections import Counter counter = Counter(A) Третий способ: from collections import defaultdict counter = defaultdict(int) for elem in A: counter[elem] += 1 Оценка сложности алгоритмов Стоимость составления списка-счетчика: нужно n раз вставить в словарь значения. Вставк состоит из двух операций: сначала проверка, есть ли такой номер в словаре и, собственно вставка - все вместе O(1) среднем или O(n) в худшем для редких случаев, когда у всех элементов одинаковый хеш. То есть стоимость составления счетчика - O(n) в среднем, O(n^2) в худшем. Следущий шаг - отфильтровать только нужное. В худшем случае нужно пройти по всем счетчику - снова n операций по O(1) или в худшем O(n) - взять из словаря, сравнить с единицей, записать в новый словарь. В среднем O(n). Итого O(n) в среднем или для специально подготовленных данных O(n^2) в худшем. Результаты бенчмарков Обновление с большим массивом: Минутка замеров: import timeit non_Counter = \ """counter = {} for elem in A: counter[elem] = counter.get(elem, 0) + 1""" setup = "import random\n" \ "A = [random.randint(0, 100) for r in range(10**6)]" print(timeit.repeat(non_Counter, setup=setup, number=10)) non_Counter = """Counter(A)""" setup = "import random\n" \ "from collections import Counter\n"\ "A = [random.randint(0, 100) for r in range(10**6)]\n" print(timeit.repeat(non_Counter, setup=setup, number=10)) non_Counter = \ """counter = defaultdict(int) for elem in A: counter[elem] += 1""" setup = "import random\n" \ "from collections import defaultdict\n" \ "A = [random.randint(0, 100) for r in range(10**6)]" print(timeit.repeat(non_Counter, setup=setup, number=10)) Результат: [2.461800295429222, 2.456825704148736, 2.50377292183442] [0.7278253601108151, 0.7268121314832463, 0.7283143209274385] [1.3038446032438102, 1.3117127258723897, 1.3013156371393428] Как видно из результатов, быстрее всех решение с Counter. Почему такие результаты Объяснение проигрыша наивного решения со словарем: 1) Для того, чтобы получить значение из словаря, необходим хеш переменной elem. Значени хеша необходимо дважды: для того, чтобы получить предыдущее значение и для того, чтобы установить новое. Очевидно, вычислять два хеша - производить двойную работу. Замеры: non_Counter = \ """ args = [None, None] for elem in A: hash(elem) hash(elem)""" setup = "import random\n" \ "A = [random.randint(0, 100) for r in range(10**6)]\n" \ "counter = {}" print(timeit.repeat(non_Counter, setup=setup, number=10)) [1.4283945417028974, 1.433934455438878, 1.4188164931286842] Как видно, лишнее вычисление съедает 0.7 секунд или 30% от общего времени. К сожалению нет стандартной возможности получить значение из словаря по значению хеша. В класс Counter функция подсчета написана на более низком уровне (https://hg.python.org/cpython/file/tip/Modules/_collectionsmodule.c#l2204) и вызывает функции _PyDict_GetItem_KnownHash, _PyDict_SetItem_KnownHash, что значительно экономит время. 2) Каждый раз при вызове метода get(elem, 0) вызывается инструкция LOAD_ATTR (https://hg.python.org/cpython/file/ed76eac4491e/Python/ceval.c#l2253), которая должна найти нужный метод по имени. Так как метод не изменится, можно вынести его поиск за цикл: getter = counter.get for elem in A: counter[elem] = getter(elem, 0) + 1 [1.917134484341348, 1.9207427770511107, 1.9153613342431033] Удалось сэкономить еще 0.6 секунд.

Ответ 2



Есть же уже готовый Counter в модуле collections. from collections import Counter c = Counter([10, 10, 23, 10, 123, 66, 78, 123]) print(c) получаем вот что: Counter({10: 3, 123: 2, 66: 1, 78: 1, 23: 1})

Ответ 3



Для случая, когда входной список отсортирован, можно использовать itertools.groupby() вместо {el: count for el, count in collections.Counter(L).items() if count > 1}: #!/usr/bin/env python from itertools import groupby L = [10, 10, 23, 10, 123, 66, 78, 123] duplicates = {} for el, group in groupby(sorted(L)): count = len(list(group)) if count > 1: duplicates[el] = count # element -> number of occurrences print(duplicates) # -> {10: 3, 123: 2} Если список неотсортирован, то сортировка это O(n * log n) операция. На практик следует измерять производительность разных вариантов, если производительность этого кода имеет значение в вашем случае (так как для небольшого n, O(n * log n) операция может быть быстрее O(n) операции, такой как с использованием Counter()).

Ответ 4



захотелось сравнить производительность для массива состоящего из 1.000.000 элементов: Setup: from collections import Counter import pandas as pd # random list (length: 1.000.000) l = np.random.randint(1,100, 10**6).tolist() # pandas DF df = pd.DataFrame({'val':l}) # dict solution def dct(A): counter = {} for elem in A: counter[elem] = counter.get(elem, 0) + 1 return {key: counter[key] for key in filter(lambda elem: counter[elem] > 1, counter)} Timing: In [79]: %timeit Counter(l) 10 loops, best of 3: 48 ms per loop сам по себе Counter - достаточно быстрый, но нам еще надо будет отфильтровать результат ... In [80]: %timeit dct(l) 10 loops, best of 3: 178 ms per loop In [81]: %timeit df.val.value_counts().reset_index().query('val > 1').rename(columns={'index':'val', 'val':'count'}) 100 loops, best of 3: 14.4 ms per loop Pandas demonstration: In [71]: df = pd.DataFrame({'val': [10, 10, 23, 10, 123, 66, 78, 123]}) In [72]: %paste (df.val .value_counts() .reset_index() .query('val > 1') .rename(columns={'index':'val', 'val':'count'}) ) ## -- End pasted text -- Out[72]: val count 0 10 3 1 123 2

Ответ 5



Если скорость выполнения не важна, то можно сделать так: def test(lst): return {a: lst.count(a) for a in set(lst) if lst.count(a) > 1} print(test([10, 10, 23, 10, 123, 66, 78, 123])) Вывод: {10: 3, 123: 2}

понедельник, 15 июля 2019 г.

Ревью библиотеки двусвязных списков

Давеча написал зачаток библиотеки для работы с двусвязными списками. Товарищи, дайте критику:
// ---------------------------------------------- // // Doubly linked list implementation by maksspace // // ---------------------------------------------- //
#ifndef maksspace_list_h #define maksspace_list_h
typedef struct { void *prev, *next; } node_link_t;
/* this macros through the list starting from the $head while maintaining the current node in $current */ #define list_foreach(head, current) \ for(typeof(head) current = head; current != NULL; current = current->next)
/* insert $node before $head, and return pointer on new head of list */ void* list_prepend(void* node, void* head) { *(node_link_t*)node = (node_link_t){NULL, head}; return ((node_link_t*)head)->prev = node; }
/* insert $node after $tail, and return pointer on new tail of list */ void* list_append(void* tail, void* node) { *(node_link_t*)node = (node_link_t){tail, NULL}; return ((node_link_t*)tail)->next = node; }
/* insert $new_node between $prev and $next, return pointer on $new_node */ void* list_insert(void* new_node, void* prev, void* next) { *(node_link_t*)new_node = (node_link_t){prev, next}; return ((node_link_t*)prev)->next = ((node_link_t*)next)->prev = new_node; }
/* delete nodes between $from and $to */ void list_nodes_del(void* from, void* to) { ((node_link_t*)from)->next = to; ((node_link_t*)to)->prev = from; }
/* ----------------------------------- * Each node of the list must contain: * struct node_name *prev, *next; * at the beginning. * ----------------------------------- * Example:
typedef struct your_node_name { struct your_node_name *prev, *next; int my_cool_int; float my_cool_float; ... struct { ... } name; } me_node_name; */
#endif /* maksspace_list_h */


Ответ

Посмотрел ваш https://github.com/maksspace/mylist
list_insert(void* new, void* prev, void* next), возвращающий new, какой-то неочевидный.
Понимаете, если между prev и next есть элементы списка, то они теряются. Я бы подумал над тем, чтобы возвращать исключаемый подсписок (причем с аккуратно модифицированными головой и хвостом) и переименовал этот insert во что-то более отражающее суть дела.
В таком случае аналогичные изменения надо внести и в void list_nodes_del(void* from, void* to)
И мелочь, конечно, но new я бы заменил, поскольку в крестах это ключевое слово.
(за смелость +1)
И еще, а почему, собственно
Each node of the list must contain: struct node_name *prev, *next; at the beginning.
IMHO Ваш код позволяет вставлять эти структуры связи в любое место структур, образующих список (и также, как и linux/list.h, иметь в структуре много разных связей, т.е. одна структура может входить в несколько разных списков).

пятница, 12 июля 2019 г.

Найти самую длинную последовательность повторяющихся чисел в списке

Задание: "Создать однонаправленный или двунаправленный список и найти самую длинную последовательность повторяющихся чисел в списке, сохранить повторяющееся значение и количество повторений"
Список вроде создал, да и на примере массивов понятно, как выполнить вторую (интересующую) часть задания, но вот с реализацией алгоритма под списки возникли трудности. Проблема в том, что я знаю, как написать код для нахождения повторяющихся значений в массиве, а как это делается для списков - не имею понятия.
Буду рад любой помощи, ибо уже немного отчаялся найти решение :(
#include #include
using namespace std;
const int size = 8;
struct Node { int Data; Node *next; Node *prev; };
int main() { Node *Start = NULL; Node *End = NULL; Node *Current = NULL;
int i = 0; while (i < size) { // Создаем Current = new Node; Current -> Data = rand() % 5 + 1; Current -> prev = NULL; Current -> next = NULL;
// Заполняем if (Start == NULL) Start = End = Current; else { End -> next = Current; Current -> prev = End; End = Current; } i++; }
// Выводим Current = Start; while (Current) { cout << Current -> Data << endl; Current = Current -> next; }
// Удаляем Current = End; while (Current) { Node *temp = Current; Current = Current -> prev; delete temp; } Start = End = NULL; return 0; }


Ответ

Вот, набросал нечто такое:
int elem_curr = Start->Data; int elem_prev = Start->Data; std::size_t length_curr = 1; std::size_t length_prev = 1; for(Current = Start; Current != nullptr; Current = Current->next) { if(Current == Start) continue; if(elem_curr == Current->Data) length_curr++; else { if(length_curr > length_prev) { elem_prev = elem_curr; length_prev = length_curr; } elem_curr = Current->Data; length_curr = 1; } }
if(length_curr > length_prev) { elem_prev = elem_curr; length_prev = length_curr; }
std::cout << elem_prev << ' ' << length_prev << std::endl;
И в начале функции main добавьте строчку srand(unsigned(time(NULL))); для рандомайзера
По коду - оптимизируйте немного, я так совсем грубо накидал, только ради основной идеи - проход по циклу + поиск подпоследовательности. Например, строчку с continue можно "замять" при небольших дополнительных действиях и проверку после цикла на посл., которая в конце списка - тоже можете организовать покрасивее в цикле. Удачи.

четверг, 11 июля 2019 г.

Формирование вложенных в список списков из других списков по определенным правилам?

Добрый день, есть три списка
allIndex_A = [1, 2, 8, 12, 17, 21, 23, 29] allIndex_B = [3, 5, 7, 9, 11, 13, 15, 18, 20, 22, 24, 26, 28, 30] allIndex_C = [4, 6, 10, 14, 16, 19, 25, 27]
нужно из них сделать список со вложенными списками, вот такой:
newList = [[1,3,4], [2,3,4], [8,9,10], [12,13,14], ...]
Первый элемент вложенного списка это первый это первый элемент списка allIndex_A. Второй элемент это первый элемент из списка allIndex_В, значение которого больше значения первого элемента из вложенного списка. Третий элемент это также первый найденный элемент из списка allIndex_С, значение которого больше первого элемента во вложенном списке.
Первый элемент второго вложенного списка это второй элемент из списка allIndex_A. Второй и третий элемент второго вложенного списка находятся по таким же условиям как и в первом вложенном списке. То есть это должен быть первый найденный элемент из списков allIndex_В и allIndex_С, значение которого больше значения первого элемента.
Пример получившегося списка newList выше. Начал изучать программирование недавно, многих вещей еще не знаю. Буду благодарен, если кто-то объяснит как сделать то, что мне нужно или хотя-бы даст ссылку где можно почитать на тему вопроса.


Ответ

Вот решение, основанное на использовании zip(), чтобы транспонировать элементы трёх списков
#!/usr/bin/env python from itertools import tee
def gt_first(first, iterable): """Yield items from iterable that are greater than corresponding items from first.
first, iterable are sorted. """ it = iter(iterable) x = next(it) #NOTE: wrap with try/except StopIteration for pep-479 for item in first: while not (x > item): x = next(it) yield x
allIndex_A = [1, 2, 8, 12, 17, 21, 23, 29] allIndex_B = [3, 5, 7, 9, 11, 13, 15, 18, 20, 22, 24, 26, 28, 30] allIndex_C = [4, 6, 10, 14, 16, 19, 25, 27]
a, ab, ac = tee(allIndex_A, 3) L = list(map(list, zip(a, gt_first(ab, allIndex_B), gt_first(ac, allIndex_C)))) print(L)
Результат
[[1, 3, 4], [2, 3, 4], [8, 9, 10], [12, 13, 14], [17, 18, 19], [21, 22, 25], [23, 24, 25]]
В ответе нет вложенного списка [29, 30, None], так как allIndex_C не имеет элемента большего 29 (можно itertools.zip_longest() вместо zip() использовать, чтобы подставить какое-нибудь значение, вместо игнорирования вложенного списка целиком).
Время исполнения линейное (O(n)). Если входные списки не отсортированы, то их надо отсортировать, тогда время исполнения O(n*log n) (на сортировку).

Циклический список для представления людей

Только начал разбираться со списками. Один момент в упор не понимаю. В книге Седжвика "Алгоритмы на С++" есть такой пример создания циклического списка для представления людей, расставленных в круг:
struct node { int item; node *next;
node(int x, node *t){ item = x; next = t; } };
int main() { typedef node *link; link t = new node(1, 0); t->next = t; link x = t;
for (int i = 2; i < 5; i++) x = (x->next = new node(i, t));
while (x != x->next){ for (int i = 1; i < 5; i++) x = x->next; x->next = x->next->next; }
cout << x->item << endl;
delete t; return 0; }
Что делает этот блок:
link t = new node(1, 0); t->next = t; link x = t;
1 строка: создается указатель на экземпляр класса node, инициализируются его поля и выделяется память;
2 строка: аналогично (*t).next = t; В классе node есть поле next - указатель на node, которому присваивается t - указатель на экземпляра класса node? Можете объяснить, зачем это делается?
3 строка: аналогична строке link x = new node(1, 0); - первой строке?
P.S. И что означает x->next = x->next->next;? Следующему элементу в списке присваивается значение, хранящееся после следующего элемента списка:
(*x).next = (*(*x).next)).next;


Ответ

Как вы сами написали, создается кольцевой список. А это означает, что последний элемент списка указывает на первый элемент списка. Так сказать, все встали в круг и взялись за руки.:)
В этих предложениях
link t = new node(1, 0); t->next = t;
создается первый элемент списка
link t = new node(1, 0);
который указывает сам на себя
t->next = t;
Затем в цикле создаются еще три элемента
link x = t; for (int i = 2; i < 5; i++) x = (x->next = new node(i, t));
Каждый раз при добавлении нового элемента его поле next указывает на первый элемент
new node(i, t)
Указатель этого созданного элемента присваивается полю next предыдущего элемента, а переменная x получает значение созданного указателя.
Можно вставить отладочную печать
#include
struct node { int item; node *next;
node(int x, node *t){ item = x; next = t; } };
int main() { typedef node *link;
link t = new node( 1, 0 ); t->next = t; link x = t;
for ( int i = 2; i < 5; i++ ) { x = ( x->next = new node( i, t) ); }
do { x = x->next; std::cout << "current: " << ( void * )x << ", value: " << x->item << ", next: " << ( void * )x->next << std::endl; } while ( x->next != t );
return 0; }
И получить примерно следующий вывод на консоль
current: 0x9cc570, value: 1, next: 0x9cc590 current: 0x9cc590, value: 2, next: 0x9cbda0 current: 0x9cbda0, value: 3, next: 0x9cbdc0 current: 0x9cbdc0, value: 4, next: 0x9cc570
Как видно, последний элемент в списке указывает на первый элемент в списке, который имеет адрес 0x9cc570
В этой тестовой программе цикл do-while начинается с предложения
x = x->next;
потому что перед этим в другом цикле при создании элементов x - это указатель последнего созданного элемента и его поле next указывает на первый элемент списка. Потому данное предложение при первой итерации цикла как раз и переходит на первый элемент списка.
Экспериментируйте с программой из книги и смотрите, что получается!