【问题标题】:Best data structure for a sparse ordered 2D array of floats allowing interpolation (perl)允许插值的稀疏有序二维浮点数组的最佳数据结构(perl)
【发布时间】:2018-11-09 21:20:55
【问题描述】:

数据是股票期权。我想根据到期前的天数(int)和标准化距离(浮动)制作一个二维数组,其值是标准化买入价和卖出价的列表。如果所需的元素不在数组中,我希望能够在最近的元素之间进行插值。

我看到了 3 种可能的数据结构:

  1. 一个稀疏的二维数组,可能有 10000 个元素,可能是 1/3。

  2. 一个二维链表,即:每个数据元素有 4 个列表指针(因此 3000 个元素变为 15000 个)

  3. 一个二维散列(可能有 3000 个元素),每个维度有 2 个排序的键列表(每个可能有 100 个元素)。

主要问题是需要插值时的有效检索。 使用任何方法检索现有元素都相对简单。

我目前正在使用选项 3,但检索有点麻烦,因为我必须沿着每个维度的键列表扫描,直到找到被占用的元素,然后进行 2 或 4 路插值。 我使用 moreUtils::firstindx($_ > $desiredKey) 来查找密钥。链表(选择 2)可以让我省去对键列表数组的搜索。

选择 1 也需要扫描,不需要键列表查找的初始步骤,但可能需要查看更多空单元格。而且插入会很麻烦。

我会做比插入更多的搜索。

有人对最有效的数据结构有什么建议吗?

【问题讨论】:

  • 是否需要插入到列表中,是插值结果还是其他元素?如果没有,排序数组 + binsearch 听起来很棒。编写一个 binsearch 很容易,这样它就会返回如果找不到该元素的索引(如 here 所示)。这需要最多 log(N) 比较(3000 个元素约 12 个),而不是您现有的方法可能需要最多 N 个比较(3000 个 3000 个元素)。在 C 中插入需要的 memcpy-ing 少于 N/2 个指针,所以仍然相当快。
  • 还可以考虑使用数据库(例如 sqlite)。它应该让您以最少的努力接近最高效率。
  • 感谢 ikegami - 我不确定如何对二维数组进行排序。至于我现在使用的键列表数组,它们足够短(
  • Re "我不确定如何对二维数组进行排序。",根据您的搜索。如果您可以在两点之间插入结果,那么点必须有顺序,对吗?通过什么查找? /// 如果您点击我发布的链接,它还显示了如何插入排序数组。它是 O(N),因此它的扩展性比 append+re-sort (O(N log N)) 略好,而且速度也快得多。 /// firstidx 必然是线性搜索。事实上,简单地调用 sub 是 O(N),因为它将 N+1 个参数放在堆栈上。 /// 你可以创建一个内存表。 CREATE TEMPORARY TABLE, IIRC
  • 所以不是 4 个最近的元素,而是 2 个最近的生命周期和 2 个最近的距离?

标签: perl data-structures


【解决方案1】:

由于您主要执行按寿命查找和按距离查找,并且插入很少,因此我将使用排序数组通过二进制搜索查找记录。

  • 定位现有元素:O(log N)
  • 定位缺失元素的框:O(log N)
  • 插入:O(N)

给定,

my @data = (
   [ $lifespan0, $distance0, $bid0, $ask0 ],
   [ $lifespan1, $distance1, $bid1, $ask1 ],
   ...
);

my $lifespan_search_cmp = sub { $a <=> $data[$b][0] };
my $distance_search_cmp = sub { $a <=> $data[$b][1] };

首先,创建索引:

my @by_lifespan = sort { $data[$a][0] <=> $data[$b][0] } 0..$#data;
my @by_distance = sort { $data[$a][1] <=> $data[$b][1] } 0..$#data;

查找:

my $i = binsearch_first \&$lifespan_search_cmp, $lifespan, @by_lifespan;
my $j = binsearch_first \&$distance_search_cmp, $distance, @by_distance;

my @lifespan_matching_idxs = get_run_forward \&$lifespan_search_cmp, $lifespan, $i, @by_lifespan;
my @distance_matching_idxs = get_run_forward \&$distance_search_cmp, $distance, $j, @by_distance;

my @cross_match_idxs = do {
   my %lifespan_matching_idxs = map { $_ => 1 } @lifespan_matching_idxs;
   grep { $lifespan_matching_idxs{$_} }
      @distance_matching_idxs
};

if (@cross_match_idxs) {
   # Exact match(es) found.
   ...
} else {
   my $lifespan_lowerbracket;
   my $lifespan_upperbracket;
   if ($i >= 0) {
      $lifespan_lowerbracket = $lifespan;
      $lifespan_upperbracket = $lifespan;
   } else {
      die "Can't interpolate" if ~$i == 0 || ~$i >= @by_lifespan;
      $lifespan_lowerbracket = $data[~$i    ][0];
      $lifespan_lowerbracket = $data[~$i - 1][0];
   }

   my $distance_lowerbracket;
   my $distance_upperbracket;
   if ($i >= 0) {
      $distance_lowerbracket = $distance;
      $distance_upperbracket = $distance;
   } else {
      die "Can't interpolate" if ~$j == 0 || ~$j >= @by_distance;
      $distance_lowerbracket = $data[~$j    ][1];
      $distance_upperbracket = $data[~$j - 1][1];
   }

   ...
}

插入:

my $i = binsearch_first \&$lifespan_search_cmp, $lifespan, @by_lifespan;
my $j = binsearch_first \&$distance_search_cmp, $distance, @by_distance;

push @data, [ $lifespan, $distance , $bid, $ask ];

splice(@by_lifespan, $i >= 0 ? $i : ~$i, 0, $#data);
splice(@by_distance, $j >= 0 ? $j : ~$j, 0, $#data);

下属:

sub binsearch_first(&$\@) {
   my  $compare = $_[0];
   #my $value   = $_[1];
   my  $array   = $_[2];

   my $min = 0;
   my $max = $#$array;
   return -1 if $max == -1;

   my $ap = do { no strict 'refs'; \*{caller().'::a'} };  local *$ap;
   my $bp = do { no strict 'refs'; \*{caller().'::b'} };  local *$bp;

   *$ap = \($_[1]);
   while ($min <= $max) {
      my $mid = int(($min+$max)/2);
      *$bp = \($array->[$mid]);

      my $cmp = $compare->();
      if ($cmp < 0) {
         $max = $mid - 1;
      }
      elsif ($cmp > 0) {
         $min = $mid + 1;
      }
      else {
         return $mid if $mid == $min;
         $max = $mid;
      }
   }

   # Converts unsigned int to signed int.
   return unpack('j', pack('J', ~$min));
}

sub get_run_forward(&$\@) {
   my  $compare = $_[0];
   #my $value   = $_[1];
   my  $start   = $_[2];
   my  $array   = $_[3];

   return if $start < 0;

   my $ap = do { no strict 'refs'; \*{caller().'::a'} };  local *$ap;
   my $bp = do { no strict 'refs'; \*{caller().'::b'} };  local *$bp;

   *$ap = \($_[1]);

   my $i = $start;
   while ($i <= $#$array) {
      *$bp = \($array->[$i]);

      my $cmp = $compare->()
         and last;

      ++$i;
   }

   return wantarray ? ($start..$i-1) : $i-1;
}

您可能希望在浮点比较中使用容差(即在 $distance_search_cmp 中)。

【讨论】:

  • 感谢池上!你真的花了一些时间在这上面!你的解决方案看起来有点像我正在做的,除了我的代码是一个 kloodge,而你的代码很优雅。 :-) 我试试看,
  • 不客气。查看 CPAN 以获取基于 c 的 binsearch 以加快速度。如果有多个结果,请确保它始终找到第一个结果
  • 您可以通过使用按寿命然后按距离排序的第三个查找数组来消除对get_run_forward 的需要。元素的值可以是一个索引数组来处理具有相同寿命和距离的元素。
猜你喜欢
  • 2018-08-13
  • 2015-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-04
相关资源
最近更新 更多