我正在努力优化对具有“几乎”排序数据的数据结构的查找。我相当有信心,它的“几乎”细节实际上并不重要,但是不确定
实际的数据结构比SO所需的更为复杂,因此我对其进行了简化。简化版本为std::vector<Level>
,其中包含价格,出价和要价:
当我一般地说时,我的意思是数据具有较长的序列,通常为零,后跟有意义的值,但是某些零实际上可能是负数。但是,我只会搜索正值,因此所有零和负数都不是有意义的返回值
以下是我的简化程序中针对SO的测试数据:
// Price Bid Ask Index
levels.emplace_back(Level( 42.0, 0, 150)); // 0
levels.emplace_back(Level( 43.0, 0, 71)); // 1
levels.emplace_back(Level( 44.0, 0, 70)); // 2
levels.emplace_back(Level( 45.0, 0, 70)); // 3
levels.emplace_back(Level( 46.0, 0, 69)); // 4
levels.emplace_back(Level( 47.0, 0, 0)); // 5
levels.emplace_back(Level( 48.0, -1, -1)); // 6
levels.emplace_back(Level( 49.0, 0, 0)); // 7
levels.emplace_back(Level( 50.0, 80, 0)); // 8
levels.emplace_back(Level( 51.0, 81, 0)); // 9
levels.emplace_back(Level( 52.0, 81, 0)); // 10
levels.emplace_back(Level( 53.0, 82, 0)); // 11
levels.emplace_back(Level( 54.0, 201, 0)); // 12
当我在此结构中搜索某些出价(“搜索出价”)时,我想查找出价大于或等于“搜索出价”的第一个级别的价格
当我在此结构中搜索某些“问询”时,我想查找其卖价大于或等于“求知”的最后一个关卡的价格
以下是我的SO简化程序:
#include <algorithm>
#include <iostream>
#include <vector>
struct Level final {
Level() = delete;
Level(const double a_price, const int a_bid, const int a_ask) :
m_price(a_price),
m_bid (a_bid),
m_ask (a_ask)
{}
const double m_price;
const int m_bid;
const int m_ask;
};
int main(int argc, char** argv) {
if (argc != 3) {
std::cout << "Usage: " << argv[0] << " <Seek Bid> <Seek Ask>\n";
exit(1);
}
std::vector<Level> levels;
// Price Bid Ask Index
levels.emplace_back(Level( 42.0, 0, 150)); // 0
levels.emplace_back(Level( 43.0, 0, 71)); // 1
levels.emplace_back(Level( 44.0, 0, 70)); // 2
levels.emplace_back(Level( 45.0, 0, 70)); // 3
levels.emplace_back(Level( 46.0, 0, 69)); // 4
levels.emplace_back(Level( 47.0, 0, 0)); // 5
levels.emplace_back(Level( 48.0, -1, -1)); // 6
levels.emplace_back(Level( 49.0, 0, 0)); // 7
levels.emplace_back(Level( 50.0, 80, 0)); // 8
levels.emplace_back(Level( 51.0, 81, 0)); // 9
levels.emplace_back(Level( 52.0, 81, 0)); // 10
levels.emplace_back(Level( 53.0, 82, 0)); // 11
levels.emplace_back(Level( 54.0, 201, 0)); // 12
const int seekBid = atoi(argv[1]);
const int seekAsk = atoi(argv[2]);
std::cout << "Seek Bid: " << seekBid << ", Seek Ask: " << seekAsk << '\n';
if (seekBid <= 0 || seekAsk <= 0) {
std::cout << "Seek Bid or Seek Ask is not positive\n";
exit(1);
}
// If the last Level's Bid is < Seek Bid then what I am looking for doesn't exist
if (levels.back().m_bid < seekBid)
std::cout << "Cannot satisfy Seek Bid\n";
else {
// Find the first Level with a Bid <= Seek Bid
// Not sure why I need to specify < instead of <= but appears to work
const auto it = std::lower_bound(
levels.begin(),
levels.end(),
seekBid,
[](const Level& a_level, const int a_bid) { return a_level.m_bid < a_bid; }
);
std::cout << "Bid Price: " << it->m_price << ", Bid Index: " << &*it - &levels[0] << '\n';
}
// If the first Level's Ask is < Seek Ask then what I am looking for doesn't exist
if (levels.front().m_ask < seekAsk)
std::cout << "Cannot satisfy Seek Ask\n";
else {
// Find the last Level with Ask <= Seek Ask
// Need to use std::prev due to how std::upper_bound works
// Not sure why I need to specify < instead of <= but appears to work
const auto it = std::prev(std::upper_bound(
levels.begin(),
levels.end(),
seekAsk,
[](const int a_ask, const Level& a_level) { return a_level.m_ask < a_ask; }
));
std::cout << "Ask Price: " << it->m_price << ", Ask Index: " << &*it - &levels[0] << '\n';
}
return 0;
}
下面是一些运行我的SO测试程序的示例。 “ Seek Bid”为81而“ Seek Ask”为70的情况非常重要,因为有两个81投标和两个70 Ask。在实际程序中,找到前81个出价和后70个Ask很重要:
Seek Bid: 79, Seek Ask: 68
Bid Price: 50, Bid Index: 8
Ask Price: 46, Ask Index: 4
Seek Bid: 80, Seek Ask: 69
Bid Price: 50, Bid Index: 8
Ask Price: 46, Ask Index: 4
Seek Bid: 81, Seek Ask: 70
Bid Price: 51, Bid Index: 9
Ask Price: 45, Ask Index: 3
Seek Bid: 82, Seek Ask: 71
Bid Price: 53, Bid Index: 11
Ask Price: 43, Ask Index: 1
所有这些结果都是正确的,但这是我的问题:
std::lower_bound
或std::upper_bound
认为我只是
寻找正值?换句话说,做负面的
根据我的搜索要求会导致任何不确定的行为?std::lower_bound
如何工作的描述
en.cppreference.com和cplusplus.com非常令人困惑,我只是
意识到在我的lambda中使用<
而非<=
是“正确的”
通过反复试验。如果我使用<=
为什么不正确?
寻找第一个/最后一个级别,即<=
我正在搜索的级别
为了?答案 0 :(得分:3)
几乎所有(有序的)stl容器都依赖严格的弱排序。严格的弱排序根据一项优先于另一项来定义元素的相对位置。
因此,严格的弱排序具有以下属性:
如果您希望这些STL容器和算法按指定方式工作,则您提供的比较必须提供这种严格的弱排序。
参考,更多详细信息:
https://en.cppreference.com/w/cpp/named_req/Compare
https://github.com/bashrc-real/Codearchive/blob/master/cpp/Strict_weak_ordering_and_stl.md
答案 1 :(得分:3)
一般要求在Compare中进行了描述。使用提供的比较,必须有一个单一的顺序,以使等效元素组在该顺序中具有特定位置。 lower_bound
和upper_bound
要求输入的顺序必须如此。
在搜索以确保结果正确之前,是否有必要将所有负数都设为零。
在这种特定情况下不行,因为它将仅针对给定的正值而不对彼此测试Level
。您的comp
将0
视为与-1
等效,因此它们“无序”并不重要。在此数据集中搜索0
或负数将是不确定的行为。
如果我要搜索的{/ {1}}的第一个/最后一个级别,为什么不
<=
使用“ {正确””?
因为这打破了严格弱阶的不对称要求。如果只需要较大的值,请使用<=
。
答案 2 :(得分:2)
std::lower_bound
和std::upper_bound
执行简单的二进制搜索。他们不搜索特定的元素值,而是搜索分区点。您应用std::lower_bound
的范围不需要进行排序。 requirement是:
范围
[first, last)
必须相对于表达式element < value
或comp(element, value)
进行分区,即,表达式为true
的所有元素必须在其表达式的所有元素之前表达式为false
。
在搜索...之前,是否有必要将所有负数都设为零?
不。如果element < value
为正,则您的范围始终相对于表达式value
进行分区。
如果我要搜索的{/ {1}}的第一个/最后一个级别,为什么不
<=
使用“ {正确””?
因为<=
依赖于std::lower_bound
关系,而不是<
。粗略地说,它是从<=
导出a <= b
的。