Delphi: организовать блеклист в программе

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by 1n0y, 10 Dec 2010.

  1. 1n0y

    1n0y Active Member

    Joined:
    9 May 2009
    Messages:
    276
    Likes Received:
    282
    Reputations:
    2
    deleted
     
    #1 1n0y, 10 Dec 2010
    Last edited: 17 Dec 2021
  2. GhostOnline

    GhostOnline Active Member

    Joined:
    20 Dec 2008
    Messages:
    723
    Likes Received:
    110
    Reputations:
    22
    Ты плохо изучил возможности TStringList
    Ставишь Sorted := True сразу после создания объекта(и до добавления данных) и ищешь с помощью IndexOf(тут искомая строка). Если вернет - 1 значит этой строки нет в списке.
    В отсортированном списке эта функция использует алгоритм бинарного поиска => намного быстрее перебора как у тебя

    ЗЫ а какие собственно объемы?
     
    1 person likes this.
  3. 1n0y

    1n0y Active Member

    Joined:
    9 May 2009
    Messages:
    276
    Likes Received:
    282
    Reputations:
    2
    ща попробую.

    объемы - ну, от 100к строк уже начинает заметно тормозить.


    upd: ох шикарно стало :) спасибо большое!
     
    #3 1n0y, 10 Dec 2010
    Last edited: 10 Dec 2010
  4. RedFern.89

    RedFern.89 Member

    Joined:
    20 Jan 2010
    Messages:
    575
    Likes Received:
    48
    Reputations:
    0
    1n0y, циклы сами по себе весчь медленная)
     
  5. greki_hoy

    greki_hoy Member

    Joined:
    4 Mar 2010
    Messages:
    326
    Likes Received:
    57
    Reputations:
    41
    1n0y, альтернатива двоичному поиску - поиск в таблице на дельфи перенести пять минут
    Code:
    K&R
    6.6 Просмотр таблиц
    В этом параграфе, чтобы проиллюстрировать новые аспекты применения структур, мы напишем ядро пакета программ, осуществляющих вставку элементов в таблицы и их поиск внутри таблиц. Этот пакет - типичный набор программ, с помощью которых работают с таблицами имен в любом макропроцессоре или компиляторе. Рассмотрим, например, инструкцию #define. Когда встречается строка вида 
    #define IN 1
    имя IN и замещающий его текст 1 должны запоминаться в таблице. Если затем имя IN встретится в инструкции, например в 
    state = IN;
    это должно быть заменено на 1. 
    Существуют две программы, манипулирующие с именами и замещающими их текстами. Это install(s,t), которая записывает имя s и замещающий его текст t в таблицу, где s и t - строки, и lookup(s), осуществляющая поиск s в таблице и возвращающая указатель на место, где имя s было найдено, или NULL, если s в таблице не оказалось. 
    Алгоритм основан на хэш-поиске: поступающее имя свертывается в неотрицательное число (хэш-код), которое затем используется в качестве индекса в массиве указателей. Каждый элемент этого массива является указателем на начало связанного списка блоков, описывающих имена с данным хэш-кодом. Если элемент массива равен NULL, это значит, что имен с соответствующим хэш-кодом нет. 
     
    Блок в списке - это структура, содержащая указатели на имя, на замещающий текст и на следующий блок в списке; значение NULL в указателе на следующий блок означает конец списка. 
    struct nlist {          /* элемент таблицы */
        struct nlist *next; /* указатель на следующий элемент */
         char *name;        /* определенное имя */
         char *defn;        /* замещающий текст */
    };
    А вот как записывается определение массива указателей: 
    #define HASHSIZE 101
    static struct nlist *hashtab[HASHSIZE]; /* таблица указателей */
    Функция хэширования, используемая в lookup и install, суммирует коды символов в строке и в качестве результата выдаст остаток от деления полученной суммы на размер массива указателей. Это не самая лучшая функция хэширования, но достаточно лаконичная и эффективная. 
    /* hash: получает хэш-код для строки s */
    unsigned hash(char *s)
    {
        unsigned hashval;
    
        for (hashval = 0; *s != '\0'; s++)
            hashval = *s + 31 * hashval;
        return hashval % HASHSIZE;
    }
    Беззнаковая арифметика гарантирует, что хэш-код будет неотрицательным. 
    Хэширование порождает стартовый индекс для массива hashtab; если соответствующая строка в таблице есть, она может быть обнаружена только в списке блоков, на начало которого указывает элемент массива hashtab с этим индексом. Поиск осуществляется с помощью lookup. Если lookup находит элемент с заданной строкой, то возвращает указатель на нее, если не находит, то возвращает NULL. 
    /* lookup: ищет s */
    struct nlist *lookup(char *s)
    {
        struct nlist *np;
    
        for (np = hashtab[hash(s)]; np != NULL; np = np->next)
            if (strcmp(s, np->name) == 0)
                return np; /* нашли */
        return NULL; /* не нашли */
    }
    В for-цикле функции lookup для просмотра списка используется стандартная конструкция 
    for (ptr = head; ptr != NULL; ptr = ptr->next)
        ...
    Функция install обращается к lookup, чтобы определить, имеется ли уже вставляемое имя. Если это так, то старое определение будет заменено новым. В противном случае будет образован новый элемент. Если запрос памяти для нового элемента не может быть удовлетворен, функция install выдает NULL. 
    struct nlist *lookup(char *);
    char *strdup(char *);
    
    /* install: заносит имя и текст (name, defn) в таблицу */
    struct nlist *install(char *name, char *defn)
    {
        struct nlist *np;
        unsigned hashval;
    
        if ((np = lookup(name)) == NULL) { /* не найден */
            np = (struct nlist *) malloc(sizeof(*np));
            if (np == NULL || (np->name = strdup(name)) == NULL)
                return NULL;
            hashval = hash(name);
            np->next = hashtab[hashval];
            hashtab[hashval] = np;
        } else /* уже имеется */
            free((void *) np->defn); /* освобождаем прежний defn */
        if ((np->defn = strdup(defn)) == NULL)
            return NULL;
        return np;
    }
    
     
  6. Jingo Bo

    Jingo Bo Member

    Joined:
    25 Oct 2009
    Messages:
    368
    Likes Received:
    51
    Reputations:
    7
    1n0y, сделай связанный список, состоящий из указателей на структуру из 8 байт :
    4 байт - ID
    4 байт - указатель на след. структуру.
    И все, единственно такими маленькими кусочками будет дефрагментироваться память, ну если тебе не важна скорость, то не суть.
     
    1 person likes this.
  7. M_script

    M_script Members of Antichat

    Joined:
    4 Nov 2004
    Messages:
    2,581
    Likes Received:
    1,317
    Reputations:
    1,557
    Зачем хранить 100k цифр в виде строк? Используй TList.
    Jingo Bo правильно написал.
     
    1 person likes this.
  8. GhostOnline

    GhostOnline Active Member

    Joined:
    20 Dec 2008
    Messages:
    723
    Likes Received:
    110
    Reputations:
    22
    Ну объясни тогда, мастак.
     
  9. M_script

    M_script Members of Antichat

    Joined:
    4 Nov 2004
    Messages:
    2,581
    Likes Received:
    1,317
    Reputations:
    1,557
    А то, что памяти раза в 3 меньше будет использоваться - это не преимущество?

    Все уже изобретено, TList называется.

    Метод IndexOf класса TStringList так и работает ;)
    Что быстрее - сравнить 2 строки по 8 символов или 2 восьмизначных числа?
     
  10. GhostOnline

    GhostOnline Active Member

    Joined:
    20 Dec 2008
    Messages:
    723
    Likes Received:
    110
    Reputations:
    22
    В отсортированном - нет.
     
  11. M_script

    M_script Members of Antichat

    Joined:
    4 Nov 2004
    Messages:
    2,581
    Likes Received:
    1,317
    Reputations:
    1,557
    Не подумал об этом. При поиске строки в отсортированном TStringList скорость будет такой же, как при поиске числа в TList. Остается только преимущество в объеме используемой памяти.
     
  12. GhostOnline

    GhostOnline Active Member

    Joined:
    20 Dec 2008
    Messages:
    723
    Likes Received:
    110
    Reputations:
    22
    В современных версиях дельфи есть решение намного лучше:
    TList<Integer>
    Для чисел реализован дефолтный компаратор.
    И расходов памяти лишних не будет, и сравнение чисел работает на порядок быстрее чем строк.
    Не стал об этом сразу писать, ибо тут у большинства стоит дельфи 7, и про дженерики тут слыхом не слыхивали.

    Если ТСу надо и у него модерновая дельфи то могу написать пример как лучше хранить числа.
     
  13. W!z@rD

    W!z@rD Борец за русский язык

    Joined:
    12 Feb 2006
    Messages:
    973
    Likes Received:
    290
    Reputations:
    43
    я конечно понимаю что 7 пожалуй самая удачная версия, но может быть пора с нее слазить?

    Как минимум ради Generiс коллекций...
     
  14. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    2 W!z@rD D7 (2002 год выпуска) стала на столько легендарной как и WinXP (2001 год выпуска). Видимо в те года делали продукты на века ))
    На хабре было обсуждение D7 и более новых версий.
    И почти все склонны к тому, что конкуренцию для D7 может составить только Delphi 2010
     
    #14 slesh, 15 Dec 2010
    Last edited: 15 Dec 2010
  15. flacs

    flacs Member

    Joined:
    28 Jan 2009
    Messages:
    81
    Likes Received:
    31
    Reputations:
    6
    как то привычно с D7, какой то родной он ) 2010 дольше грузица на моем компе.
     
  16. GhostOnline

    GhostOnline Active Member

    Joined:
    20 Dec 2008
    Messages:
    723
    Likes Received:
    110
    Reputations:
    22
    Ок, она грузицца на 5 сек. дольше, но сокращает время на разработку раза в два наверное.
    А если в среду ставить эксперты/визарды/плагины то разница по времени загрузки и вовсе становится неощутимой.
     
  17. 1n0y

    1n0y Active Member

    Joined:
    9 May 2009
    Messages:
    276
    Likes Received:
    282
    Reputations:
    2
    Jingo Bo, M_script! Можете примерно рассказать как в данной задаче юзать тлист? погуглил, но толком нечего и не понял. заранее благадарю :)
    стринглист-то хорош, только вот когда блек >1 ляма начинаются ошибки out of memory :(
     
  18. Jingo Bo

    Jingo Bo Member

    Joined:
    25 Oct 2009
    Messages:
    368
    Likes Received:
    51
    Reputations:
    7
    1n0y, приводить пример не буду, т.к. очередной велосипед, при том при всем писать с сортировкой и организацией списка строк 200 не хочется. Гуглить надо "Delphi связанный список", элементом которого является запись из Cardinal числа. Еще хочу посоветовать хранить не только начальный, но и конечный элемент списка, для того что бы определение по индексу было быстрее. Допустим есть у нас список из 100 элементов, нужен 80-ый, что бы до него добраться - быстрее с конечного спускаться, чем с начального подниматься. И второй совет - выделяй память блоками, а не по одному элементу, так память будет меньше дефрагментироваться, конечно тут придется подумать как лучше это реализовать.
     
  19. 1n0y

    1n0y Active Member

    Joined:
    9 May 2009
    Messages:
    276
    Likes Received:
    282
    Reputations:
    2
    спасибо!
    погуглив, решил всёже отложить это дело. допилил немного блеклист из стринлиста - теперь даже с >2 лямами работает стабильно. посмотрим как пойдет :)
     
  20. M_script

    M_script Members of Antichat

    Joined:
    4 Nov 2004
    Messages:
    2,581
    Likes Received:
    1,317
    Reputations:
    1,557
    Нашел простое и эффективное решение для блэк-листа ВК - массив битов.
    Огромная скорость и список ID любого объема взамен на 16 метров памяти.
    Пример:
    PHP:
    long black[1024 1024];
    //---------------------------------------------------------------------------
    bool is_black(long id)
    {
        return ( 
    black[id/32] & (<< id%32) ) ? true false;
    }
    //---------------------------------------------------------------------------
    void add_black(long id)
    {
        
    black[id/32] |= (<< id%32);
    }
    //---------------------------------------------------------------------------
    int main()
    {
        
    memset(&black[0], 1024 10240);

        
    char buf[256] = {0};
        
    FILEfopen("black.txt""r");
        while(
    fgets(buf255f))
            
    add_black(atol(buf));
        
        
    long id 0;
        
    cin >> id;
        
    cout << is_black(id);
        
        return 
    0;
    }
    //---------------------------------------------------------------------------
     
    #20 M_script, 20 Dec 2010
    Last edited: 21 Dec 2010