LINUX.ORG.RU

Как сдвинуть std::lower_bound ?

 ,


0

2

Приветствую )

Имеется большой список со значениями (пускай будет vector, хотя использую deque) - как бы найти первый ближайший элемент, который меньше или равен значению???

На основании (а так же из-за наличия только 11/14) запили реплику

    ssize_t cnt = q.size() - 1;
    size_t m, l = 0, i = 0;
    while (cnt > 0)
    {
        i = l;
        m = cnt / 2;
        i += m;
        if (q.at(i) < val)
        {
            l = ++i;
            cnt -= m + 1
        }
        else
            cnt = m;
    }

все бы хорошо, НО находится всегда первый после val, std::upper_bound тоже пробовал реализовать, но всегда получаю последний элемент, поскольку почти никогда не имею значения val в массиве.

З.Ы. сдвинуться на назад можно на элемент, но хочется понять почему при бинарном поиске получается индекс справа, а не слева!?

или например еще один из вариантов бинарного поиска

    size_t m, l = 0, r = q.size() - 1;
    while (l < r)
    {
        m = (l + r) / 2;
        if (q.at(m) < val)
            l = m + 1;
        else if (q.at(m) > val)
            r = m - 1;
        else
            break;
    }

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

З.Ы. и тут результат конечно можно перепроверить после цикла, но как бы гарантированно сразу получить элемент слева от числа???

★★★

Последнее исправление: wolverin (всего исправлений: 2)

Ответ на: комментарий от anonymous

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

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 1)

например еще один из вариантов бинарного поиска … очевидно в зависимости от результата целочисленного деления.

Я несколько разочарован, прямо скажем: в столь коротком фрагменте как минимум две серьёзные логические ошибки, одно переполнение, и вишенкой на торте - пессимизация на пустом месте. Неужели не видите?

bugfixer ★★★★★
()
Ответ на: комментарий от wolverin

мотороллер не мой, я просто разместил объяву

Руки бы отрывал за такие статейки, из рук вон плохо написано. Возможно специально мины расставили чтобы заставить народ подумать.

Приведённый binsearch_array() не будет работать как lower_bound(), в частности из-за

        } else if (a[m] > val) {
            r = m - 1;
        }

Нельзя здесь исключать «m» - он запросто может оказаться искомым индексом в случае а[m-1] < val. Это, наверное, главное.

Если допускаются повторяющиеся элементы и нужно найти именно первое вхождение то нельзя сразу выходить по

        } else {
            return m;
        }

Нужно ждать пока диапазон поиска не схлопнется. Это вторая серьёзная ошибка.

Вот это вот

        int m = (l + r) / 2;

запросто переполняется, о чём авторы скромно умолчали. Никто так не делает. Надо «l + (r - l) / 2».

Ну а

q.at(m)

это уже ваш собственный перл. Нафиг здесь проверки диапазона не впились.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Нельзя здесь исключать

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

нужно найти именно первое вхождение

нужно найти последнее перед val

запросто переполняется

ну ни разу ничего не переполнилось

это уже ваш собственный перл

это вообще лишняя проверка на время так сказать изучения вопроса, чтобы ошибка всплыла именно в этом месте, а не где то потом еще

не будет работать как lower_bound()

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

wolverin ★★★
() автор топика
Ответ на: комментарий от wolverin

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

Не может оно там зацикливаться если диапазон поиска на каждом шаге сокращается вдвое, в этом смысле «-1» там погоды не делает абсолютно. Ошибка где-то ещё.

ну ни разу ничего не переполнилось

На достаточно длинных векторах не проверяли.

bugfixer ★★★★★
()

Примерный набросок. Почти шаблонный - заменить int на свой тип/структуру и написать compare. «Неоптимально», зато прозрачная логика бинарного поиска. Компилятор умный - оптимизирует. NULL - если не найден меньший или равный. Скорее всего, не(правильно) работает :)

int* upper_bound_prev(int* array, size_t count, int* value, int (*compare)(int*, int*)) {
  int* first = array;
  int* last = first + count;
  while (last - first > 0) {
    int* p = first;
    p += (last - first) / 2;
    if (!compare(value, p))
      first = p + 1;
    else
      last = p;
  }
  return first == array ? NULL : first;
}
anonymous
()
Ответ на: комментарий от anonymous

Вернет, естественно, индекс. Самый левый, если есть дупликаты, или место вставки, если значение не найдено == ближайший элемент слева.

anonymous
()