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

Если Вы ничего не фиксировали с тех пор, всего git rm файл и git commit --amend.

, Если Вы имеете

git filter-branch \
--index-filter 'git rm --cached --ignore-unmatch path/to/file/filename.orig' merge-point..HEAD

, пройдет каждое изменение от merge-point до HEAD, удалит filename.orig и перепишет изменение. Используя --ignore-unmatch означает, что команда не перестанет работать, если по некоторым причинам filename.orig будет отсутствовать в изменении. Это - рекомендуемый путь от раздела Examples в страница справочника .

ответвления фильтра мерзавца Примечание для пользователей Windows: путь к файлу должен наклонные черты вправо использования

22
задан 14 revs, 4 users 100% 31 August 2009 в 08:57
поделиться

9 ответов

Вы можете использовать карту от репрезентативного элемента до списка / вектора / двухсторонней очереди других элементов. Это требует относительно меньшего количества сравнений при вставке в контейнер и означает, что вы можете перебирать результирующие группы без необходимости выполнять какие-либо сравнения.

В этом примере всегда вставляется первый репрезентативный элемент в сопоставленное хранилище двухсторонней очереди, поскольку он выполняет последующую итерацию через группа логически проста, но если это дублирование доказывает проблему, тогда было бы легко выполнить push_back , только if (! ins_pair.second) .

typedef std::map<Type, std::deque<Type> > Storage;

void Insert(Storage& s, const Type& t)
{
    std::pair<Storage::iterator, bool> ins_pair( s.insert(std::make_pair(t, std::deque<Type>())) );
    ins_pair.first->second.push_back(t);
}

Итерация по группам тогда (относительно) просто и дешево:

void Iterate(const Storage& s)
{
    for (Storage::const_iterator i = s.begin(); i != s.end(); ++i)
    {
        for (std::deque<Type>::const_iterator j = i->second.begin(); j != i->second.end(); ++j)
        {
            // do something with *j
        }
    }
}

Я провел несколько экспериментов для сравнения и подсчета объектов. В тесте со 100000 объектами в случайном порядке, образующими 50000 групп (т.е. и в среднем 2 объекта на группу) вышеупомянутый метод стоил следующего количества сравнений и копий:

1630674 comparisons, 443290 copies

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

Использование мультиотображения и сохранение предыдущего элемента на последней итерации для обнаружения групповых переходов требует следующих затрат:

1756208 comparisons, 100000 copies

Использование одного списка и выталкивание переднего элемента и выполнение линейного поиска для другой группы Стоимость участников:

1885879088 comparisons, 100000 copies

Да, это ~ 1,9 млрд сравнений по сравнению с ~ 1,6 млн для моего лучшего метода. Чтобы метод списка работал где-то рядом с оптимальным числом сравнений, его нужно будет отсортировать, и это будет стоить столько же сравнений, сколько и построение изначально упорядоченного контейнера.

Правка

Я взял ваш опубликованный код и запустил подразумеваемый алгоритм (мне пришлось сделать некоторые предположения о коде, как и некоторые предполагаемые определения) на том же наборе тестовых данных, который я использовал раньше, и я подсчитал:

1885879088 comparisons, 420939 copies

, т.е. точно такое же количество сравнений, что и в моем алгоритме тупого списка, но с большим количеством копий. Я думаю, это означает, что в данном случае мы используем практически одинаковые алгоритмы. Я не вижу никаких доказательств альтернативного порядка сортировки, но похоже, что вам нужен список групп, которые содержат более одного эквивалентного элемента. Этого можно просто достичь в моей функции Iterate , добавив в if (i-> size> 1) предложение.

Я до сих пор не вижу никаких доказательств того, что построение отсортированного такой контейнер, как эта карта дека, не является хорошей (даже если не оптимальной) стратегией.

точно такое же количество сравнений, что и мой алгоритм тупого списка, но с большим количеством копий. Я думаю, это означает, что в данном случае мы используем практически одинаковые алгоритмы. Я не вижу никаких доказательств альтернативного порядка сортировки, но похоже, что вам нужен список групп, которые содержат более одного эквивалентного элемента. Этого можно просто достичь в моей функции Iterate , добавив в if (i-> size> 1) предложение.

Я до сих пор не вижу никаких доказательств того, что построение отсортированного такой контейнер, как эта карта дека, не является хорошей (даже если не оптимальной) стратегией.

точно такое же количество сравнений, что и мой алгоритм немого списка, но с большим количеством копий. Я думаю, это означает, что в данном случае мы используем практически одинаковые алгоритмы. Я не вижу никаких доказательств альтернативного порядка сортировки, но похоже, что вам нужен список групп, которые содержат более одного эквивалентного элемента. Этого можно просто достичь в моей функции Iterate , добавив в if (i-> size> 1) предложение.

Я до сих пор не вижу никаких доказательств того, что построение отсортированного такой контейнер, как эта карта дека, не является хорошей (даже если не оптимальной) стратегией.

но похоже, что вам нужен список групп, которые содержат более одного эквивалентного элемента. Этого можно просто достичь в моей функции Iterate , добавив в if (i-> size> 1) предложение.

Я до сих пор не вижу никаких доказательств того, что построение отсортированного такой контейнер, как эта карта дека, не является хорошей (даже если не оптимальной) стратегией.

но похоже, что вам нужен список групп, которые содержат более одного эквивалентного элемента. Этого можно просто достичь в моей функции Iterate , добавив в if (i-> size> 1) предложение.

Я до сих пор не вижу никаких доказательств того, что построение отсортированного такой контейнер, как эта карта дека, не является хорошей (даже если не оптимальной) стратегией.

3
ответ дан 29 November 2019 в 05:18
поделиться

Да, вы можете сделать намного лучше.

  1. Сортируйте их (O (n) для простых целых чисел, O (n * log n) в целом), то дубликаты гарантированно будут смежными, что ускоряет их поиск O (n)

  2. Используйте хеш-таблицу, также O (n). Для каждого элемента (а) проверьте, есть ли он уже в хэш-таблице; если да, то это дубликат; если нет, поместите его в хеш-таблицу.

edit

Метод, который вы используете, похоже, выполняет O (N ^ 2) сравнений:

for i = 0; i < length; ++i           // will do length times
    for j = i+1; j < length; ++j     // will do length-i times
        compare

Итак, для длины 5 вы делаете 4 + 3 + 2 + 1 = 10 сравнений; для 6 вы делаете 15 и т.д. (N ^ 2) / 2 - N / 2, если быть точным. N * log (N) меньше при любом достаточно высоком значении N.

Насколько велик N в вашем случае?

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

13
ответ дан 29 November 2019 в 05:18
поделиться

1. Сортировка массива O (n log n) в худшем случае - сортировка слиянием / heapsort / сортировка двоичного дерева и т. Д.

2. Сравните соседей и вытащите совпадения O (n)

7
ответ дан 29 November 2019 в 05:18
поделиться

Сохранять структуру на основе хеш-таблицы от значения до подсчета; Если ваша реализация C ++ не предлагает std :: hash_map (пока что не является частью стандарта C ++! -), используйте Boost или загрузите версию из Интернета. Один проход по коллекции (то есть O (N)) позволяет вам выполнить отображение значение-> счетчик; еще один проход по хеш-таблице (очевидно, <= O (N)), чтобы идентифицировать значения со счетчиком> 1 и выдавать их соответствующим образом. В целом O (N), что не соответствует вашему предложению.

5
ответ дан 29 November 2019 в 05:18
поделиться

А сортировку пробовали? Например, используя такой алгоритм, как быстрая сортировка? Если производительность для вас достаточно высока, это будет простой подход.

1
ответ дан 29 November 2019 в 05:18
поделиться

Если известно, что это список целых чисел, и если известно, что все они находятся между A и B (например, A = 0, B = 9), создайте массив BA элементы и создают контейнеры BA.

В очень конкретном случае (список простых целых чисел) я предлагаю вам просто подсчитать их, поскольку вы все равно не можете различать разные целые числа:

for(int i = 0; i < countOfNumbers; i++)
    counter[number[i]]++;

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

Если они не являются числами, используйте std :: map или std :: hash_map, сопоставив ключи со списками значений.

1
ответ дан 29 November 2019 в 05:18
поделиться

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

Если вам что-то известно о данных, возможны более эффективные алгоритмы.

Например, если вы знали, что список большой и содержит только целые числа от 1 до n, где n довольно мало, вы можете использовать пару логических массивов (или растровое изображение) и сделать что-то вроде этого:

bool[] once = new bool[MAXIMUM_VALUE];
bool[] many = new bool[MAXIMUM_VALUE];
for (int i = 0; i < list.Length; i++)
    if (once[ value[i] ])
        many[ value[i] ] = true;
    else
        once[ value[i] ] = true;

Теперь , many [] содержит массив, значения которого наблюдались более одного раза.

0
ответ дан 29 November 2019 в 05:18
поделиться

Большинство людей, упоминающих решения хеширования / неупорядоченной карты, предполагают, что время вставки и запроса составляет O (1), однако в худшем случае это может быть O (N). Кроме того, вы аннулируете стоимость хеширования объекта.

Лично я бы вставлял объекты в двоичное дерево (вставка O (logn) для каждого) и держал счетчик на каждом узле. Это дало бы время построения O (nlogn) и обход O (n) для идентификации всех дубликатов.

0
ответ дан 29 November 2019 в 05:18
поделиться

Если я правильно понял вопрос, то это самое простое решение, которое я могу придумать:

std::vector<T> input;
typedef std::vector<T>::iterator iter;

std::vector<std::pair<iter, iter> > output;

sort(input.begin(), input.end()); // O(n lg n) comparisons

for (iter cur = input.begin(); cur != input.end();){
  std::pair<iter, iter> range = equal_range(cur, input.end(), *cur); // find all elements equal to the current one O(lg n) comparisons
  cur = range.second;
  output.push_back(range);
}

Общее время работы: O (n log n) . end () также используется гораздо чаще, чем необходимо, и его можно легко кэшировать.

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

0
ответ дан 29 November 2019 в 05:18
поделиться
Другие вопросы по тегам:

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