【问题标题】:How to get the rank of millions of unsorted float number efficiently?如何有效地获得数百万个未排序浮点数的排名?
【发布时间】:2012-08-22 21:01:35
【问题描述】:

2 亿个浮点数,可能有些是重复的。

什么是获取其中每个元素排名的有效方法(例如,内存小于 1G)?

像这样:

输入:[3.2, 3.2, 3.4, 7.81, 1.0]

输出:[2, 2, 4, 5 ,1]

我想到了bitmap sort,但在这种情况下它看起来并不节省内存。

【问题讨论】:

  • 元素的“等级”到底是什么?
  • 你不太可能在远低于 1.6G 的情况下做到这一点。存储浮点数本身需要 2 亿 * 4 字节 = 800 MB,然后存储输出是另一个(至少)800 MB。
  • @dbaupp 我假设这些值最初存储在磁盘上,并且排名也将保存在磁盘上,因此不一定需要将整个数据集存储在内存中.
  • 我认为他的意思是“排名”是排序列表中数字的索引,但是有 2 个相等的值占据输出中第一个的排名(在他的示例中,3.2 都排名为 2 . 1.0 排名第 1,因为它是最小值。)
  • 位图不起作用:因为 1)你不知道一个元素可以出现多少次(嗯,最多 2 亿次,但这并没有多大帮助) 2)浮动占据连续范围。

标签: java c algorithm sorting hash


【解决方案1】:

我认为您无法在 1G 中完成所有操作。请注意,您的 200 Mvalue 数据集将占用 ~763 MiB,仅留下 ~261 MiB 可用于辅助数据。这排除了任何需要您将索引与值同时存储的方法,因为 200 个 Mvalue 的索引至少需要 28 位。实际上,您确实需要 32 位,这将占用与原始(可能是 32 位)浮点值相同的空间。

要考虑的一种方法是对原始数据执行排序,同时将决策信息记录到位图,然后用排名索引替换原始数据并使用日志反转排列。

但是,在最坏的情况下,产生的排列至少需要log2(N!) > N log2(N) - N log2(e) 位存储(因此无法通过使用基数排序或其他方式来解决它)。对于指定的问题,请注意log2(200M)>27,因此日志可能需要比(200M * 25.5) / (8bits/byte) ~ 608 MiB 更多的空间——几乎与原始数据集一样大,并且远大于指定的辅助空间。

您可以将决策日志写入磁盘,然后重新读取它以生成您的答案。但是,如果您允许磁盘 I/O,您不妨改为进行外部排序,这将允许您解决比您的内存容量大得多的问题。

【讨论】:

    【解决方案2】:

    您不想对数组进行排序,但您想获得排序后位置所在的索引数组。它需要超过 1 GB 的内存,并且您可能需要进行一些后处理才能使相等的元素具有相同的等级,但您应该能够使用此解决方案作为起点:Get the indices of an array after sorting?

    【讨论】:

      【解决方案3】:

      您可以根据 int 值对浮点数范围进行排序,例如Float.floatToRawInt(float).

      如果您有 1 GB 并且每个值存储 8 个字节,则可以对最多包含 1.28 亿个或 2^27 个值的组进行排序。这意味着您将能够通过 2^5 或 32 次对它们进行排名。

      【讨论】:

      • 顺序不一样:浮点数:-2.,-1.,0.,1.,2. -> 原始整数:-1073741824, -1082130432, 0, 1065353216, 1073741824
      • @J.F.Sebastian 我把它留给了 OP 关于如何对组进行排序的想象。如果一次按低 27 位分组,则它们都是负数或正数。
      【解决方案4】:

      您可以尝试按照 wikipedia 上的说明执行 External sorting

      在处理浮点数据时尽量使用内存映射文件。

      public static void main(String[] args) throws IOException {
          RandomAccessFile raf = new RandomAccessFile("floats.dat", "rw");
          FileChannel fc = raf.getChannel();
          MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024 * 1024 * 1024);
          FloatBuffer fb = mbb.asFloatBuffer();
          Random random = new Random();
          for (int i = 0; i < 200000000; i++) {
              float rand = random.nextFloat();
              fb.put(rand);
          }
          fb.flip();
      
          // Read data in chunks, tune the size
          float[] f = new float[100000];
          fb.get(f, 0, f.length);
      
          // Process the data using some merge strategy
      }
      

      据我了解,不应该对浮点数组本身进行排序。也可以使用内存映射文件存储 int 数组。

      【讨论】:

        【解决方案5】:

        如果您使用标准 Java 排序方法和浮点数数组,则可以使用 ~1.2GB IMO,因为它已经使用了一种非常节省空间且快速 (n lg(n)) 的排序方法 (TimSort, @ 987654322@) - 参见 Arrays.sort。

        为了使其更快,您可以将浮点数转换为整数(但您需要预先知道精度),然后应用 integer sort 或已经提到的基数排序。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-04-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-03-24
          相关资源
          最近更新 更多