Russian Qt Forum
Октябрь 02, 2024, 01:39 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: [Решено][STL] Найти ближайшее меньшее или большее значение.  (Прочитано 7248 раз)
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« : Сентябрь 16, 2013, 20:53 »

Доброго всем времени.

Задача: Есть отсортированный вектор целых значений.
Необходимо вернуть ближайшее больше или ближайшее меньшее значение относительно заданного, т.е.:

Код
C++ (Qt)
int main(int argc, char *argv[])
{
   int myints[] = {16,32,64,128,256,512,1024,2048};
   std::vector<int> v(myints,myints + 8);
 
   std::vector<int>::iterator low, up;
   low = std::near_lower(v.begin(), v.end(), 230); //
   up = std::near_upper(v.begin(), v.end(), 230); //
 
   std::cout << "near lower " << (*low) << '\n';
   std::cout << "near upper" << (*up) << '\n';
 
   return 0;
}
 

Должно вывести:
Цитировать
near lower 128
near upper 256

Есть ли в STL готовое решение (что-то типа near_lower(), near_upper())? Интересует стандарт C++98.. Или нужно самому городить огород?  Улыбающийся
« Последнее редактирование: Сентябрь 17, 2013, 13:59 от kuzulis » Записан

ArchLinux x86_64 / Win10 64 bit
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #1 : Сентябрь 16, 2013, 21:36 »

Есть find_if

Код
C++ (Qt)
   int myints[] = {16,32,64,128,256,512,1024,2048};
   std::vector<int> v(myints,myints + 8);
 
   std::vector<int>::iterator low;
 
   int val = 230;
 
   if ((low = std::find_if(v.begin(), v.end(), std::bind1st(std::less_equal<int>(), val))) != v.end()) {
       if (low != v.begin()) --low;
       std::cout << *low << std::endl;
   }
 
 

Аналогично и для near_upper
« Последнее редактирование: Сентябрь 16, 2013, 21:42 от m_ax » Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #2 : Сентябрь 16, 2013, 22:02 »

Нет, лучше так не делать..

Наверное проще свою функцию написать.. Не намного больше кода получится..

Edit:
(Это будет корректно работать, только если список отсортирован!)
« Последнее редактирование: Сентябрь 16, 2013, 22:13 от m_ax » Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
_OLEGator_
Гость
« Ответ #3 : Сентябрь 16, 2013, 22:08 »

Например, методом деления пополам искать.
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #4 : Сентябрь 16, 2013, 22:19 »

Например, методом деления пополам искать.

Только вот не для всех контейнеров это прокатит.. ( Точнее сказать, не для всех это будет эффективно..
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #5 : Сентябрь 16, 2013, 22:22 »

Да, спасибо, парни.

> Например, методом деления пополам искать.

Дело в том, что функция то уже есть, но она Си-шная и там тупо перебор каждого значения. Я хотел бы портировать ее на C++ с использованием шаблонов.

> Наверное проще свою функцию написать.. Не намного больше кода получится..

А если делать шаблонными near_lower() и near_upper() - То как будет выглядеть код? А то я в шаблонах не очень то.. Улыбающийся

Переписал на шаблонах:

Код
C++ (Qt)
template<class InputIterator, class T>
InputIterator near_lower(InputIterator first, InputIterator last, const T& val)
{
   InputIterator lower;
   if ((lower = std::find_if(first, last, std::bind1st(std::less_equal<T>(), val))) != last) {
       if (lower != first)
           --lower;
   }
   return lower;
}
 
int main(int argc, char *argv[])
{
   int myints[] = {16,32,64,128,256,512,1024,2048};
   std::vector<int> v(myints,myints + 8);
   std::vector<int>::iterator low;
   int val = 230;
   low = near_lower(v.begin(), v.end(), val);
   std::cout << "lower " << (*low);
   return 0;
}
 
« Последнее редактирование: Сентябрь 16, 2013, 22:27 от kuzulis » Записан

ArchLinux x86_64 / Win10 64 bit
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #6 : Сентябрь 16, 2013, 22:30 »


А если делать шаблонными near_lower() и near_upper() - То как будет выглядеть код? А то я в шаблонах не очень то.. Улыбающийся

Не, всё нормально.. Если вы гарантируете, что список будет отсортирован..

И ещё, конечно, нужно проверять: не вернула ли near_lower end:
Код
C++ (Qt)
if ((low = near_lower(v.begin(), v.end(), val) != v.end()) {
   std::cout << *low << std::endl;
}
 
« Последнее редактирование: Сентябрь 16, 2013, 22:33 от m_ax » Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #7 : Сентябрь 16, 2013, 22:55 »

Блин, что-то тяжко с шаблонами у меня. Нужно например так реализовать, чтобы при:

val = 1 -> low=-xxxx, up=-xxxx // выход за границы, it::end
val = 16 -> low=16, up=16
val = 17 -> low=16, up=32
val = 31 -> low=16, up=32
val = 2048 -> low=2048, up=2048
val = 2049 -> low=-xxxx, up=-xxxx // выход за границы, it::end

Такое возможно? Улыбающийся
Записан

ArchLinux x86_64 / Win10 64 bit
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #8 : Сентябрь 17, 2013, 00:59 »

Вот для произвольной последовательности:

Код
C++ (Qt)
template<class InputIterator, class T>
InputIterator near_lower(InputIterator first, InputIterator last, const T& val)
{
   InputIterator lower = first;
   while (first != last) {
       if ((val - *first >= 0) && (val - *first <= fabs(val - *lower)))
           lower = first;
       ++first;
   }
   return lower;
}
 
int main()
{
   std::vector<int> v = {161,-32,64,128,-256,512,1024,2048};
   int val = 0;
   std::vector<int>::iterator low;
 
   if ((low = near_lower(v.begin(), v.end(), val)) != v.end())
       std::cout << *low << std::endl;
 
   return 0;
}
 
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #9 : Сентябрь 17, 2013, 01:59 »

Кстати, есть же стандартные std::lower_bound  и std::upper_bound  Смеющийся
Работают только для отсортированных последовательностей..


Код
C++ (Qt)
template<class InputIterator, class T>
InputIterator near_lower_s(InputIterator first, InputIterator last, const T& val)
{
   if ((val < *first) || (first == last)) return last;
   InputIterator lower = std::lower_bound(first, last, val);
 
   return (lower == last) ? --lower : lower;
}
 


 
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #10 : Сентябрь 17, 2013, 10:10 »

Код
C++ (Qt)
int main(int argc, char *argv[])
{
   int myints[] = {16,32,64,128,256,512,1024,2048};
   std::vector<int> v(myints, myints + sizeof(myints) / sizeof(myints[0]));
 
   int resLo, resHi, match = 230;
   int * bound = std::lower_bound(v.begin(), v.end(), match);
   if (bound == v.end())
    resLo = resHi = v.back();
   else {
    if (*bound == match)
      resLo = resHi = match;
    else {
     resHi = *bound;
     resLo = (bound == v.begin()) ? resHi : *(bound - 1);
    }
   }
 
   std::cout << "near lower " << resLo << '\n';
   std::cout << "near upper" << resHi << '\n';
 
   return 0;
}
 

lower_bound (upper_bound) - одни из немногих действительно нужных std ф-ций.
« Последнее редактирование: Сентябрь 17, 2013, 10:16 от Igors » Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #11 : Сентябрь 17, 2013, 11:07 »

Спасибо парни, в итоге наваял это:

Код
C++ (Qt)
#include "stdafx.h"
 
#include <iostream>
#include <algorithm>
#include <vector>
 
template<class InputIterator, class T>
T near_lower(const InputIterator first, const InputIterator last, const T& val)
{
   InputIterator lower = std::lower_bound(first, last, val);
   if ((val <= *first) || (lower == last) || (first == last)) return val;
   if (val != *lower) --lower;
   return (*lower);
}
 
template<class InputIterator, class T>
T near_upper(const InputIterator first, const InputIterator last, const T& val)
{
   InputIterator upper = std::lower_bound(first, last, val);
   return ((val <= *first) || (upper == last) || (first == last)) ? val : (*upper);
}
 
void tst_lower(std::vector<int>::const_iterator &first, std::vector<int>::const_iterator &last)
{
   int test_sequence[] = {1,16,17,230,256,2000,2048,2049,60000};
   std::vector<int> tst(test_sequence, test_sequence + 9);
   std::vector<int>::const_iterator it = tst.begin();
   std::vector<int>::const_iterator end = tst.end();
   std::cout << std::endl << "Lower test: " << std::endl;
   for (it; it != end; ++it) {
       int low = near_lower(first, last, *it);
       std::cout << "val " << (*it) << ", lower " << low << std::endl;
   }
}
 
void tst_upper(std::vector<int>::const_iterator &first, std::vector<int>::const_iterator &last)
{
   int test_sequence[] = {1,16,17,230,256,2000,2048,2049,60000};
   std::vector<int> tst(test_sequence, test_sequence + 9);
   std::vector<int>::const_iterator it = tst.begin();
   std::vector<int>::const_iterator end = tst.end();
   std::cout << std::endl << "Upper test: " << std::endl;
   for (it; it != end; ++it) {
       int low = near_upper(first, last, *it);
       std::cout << "val " << (*it) << ", upper " << low << std::endl;
   }
}
 
 
int _tmain(int argc, _TCHAR* argv[])
{
   int myints[] = {16,32,64,128,256,512,1024,2048};
   std::vector<int> v(myints, myints + 8);
 
   tst_lower(v.begin(), v.end());
   tst_upper(v.begin(), v.end());
 
   return 0;
}
 

Вывод:
Цитировать

Lower test:
val 1, lower 1
val 16, lower 16
val 17, lower 16
val 230, lower 128
val 256, lower 256
val 2000, lower 1024
val 2048, lower 2048
val 2049, lower 2049
val 60000, lower 60000

Upper test:
val 1, upper 1
val 16, upper 16
val 17, upper 32
val 230, upper 256
val 256, upper 256
val 2000, upper 2048
val 2048, upper 2048
val 2049, upper 2049
val 60000, upper 60000

Вроде то что нужно, правда немного изменил алгоритм при выходе за границы..  Улыбающийся
Записан

ArchLinux x86_64 / Win10 64 bit
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #12 : Сентябрь 17, 2013, 11:28 »

Засовывать в вектор необязвтельно, можно просто для массива. И обычно
Код
C++ (Qt)
typedef std::vector<int>::const_iterator TIter;
а то сопля длинная получается  Улыбающийся
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #13 : Сентябрь 17, 2013, 15:17 »

Спасибки Улыбающийся
Записан

ArchLinux x86_64 / Win10 64 bit
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.289 секунд. Запросов: 23.