【问题标题】:Constructing fractions Interview challenge构建分数面试挑战
【发布时间】:2012-05-15 17:12:07
【问题描述】:

我最近遇到了以下面试问题,我想知道动态编程方法是否可行,或者/和是否有某种数学洞察力可以使解决方案更容易......它与 ieee754 如何加倍非常相似被构造出来。

问题: 有 N 个双精度值的向量 V。其中向量的第 i 个索引处的值等于 1/2^(i+1)。例如:1/2、1/4、1/8、1/16 等...

您将编写一个函数,该函数将一个双倍 'r' 作为输入,其中 0

此外,索引的数量应该最少,如果有两个解决方案,应该首选最接近零的解决方案。

void getIndexes(std::vector<double>& V, double r)
{
 .... 
}

int main()
{
   std::vector<double> V;
   // populate V...
   double r = 0.3;
   getIndexes(V,r);
   return 0;
}

注意:似乎有一些 SO'ers 没有完全阅读问题的心情。所以让大家注意以下几点:

  1. 解决方案,也就是总和可能大于 r - 因此,任何从 r 中递增减去分数,直到它达到零或接近零的策略都是错误的

  2. 有r的例子,这里会有2个解,即|r-s0| == |r-s1|并且 s0

  3. 如果您认为这个问题是微不足道的,那么您很可能还没有理解它。因此,最好再次阅读该问题。

编辑(Matthieu M.):V = {1/2, 1/4, 1/8, 1/16, 1/32} 的 2 个示例

  • r = 0.3, S = {1, 3}
  • r = 0.256652, S = {1}

【问题讨论】:

  • @amit:其实我觉得这是背包问题的简化版。
  • @MatthieuM:为了简单起见,我认为可以忽略双精度误差,基本上可以假设总和是正确的——理论上应该是这样,因为添加了2,这是单比特表示 - 但我离题了:)
  • @XanderTulip:关于您的编辑 - 进一步考虑您是正确的,这个问题可能不承认贪婪的解决方案。如果您在问题中添加具体示例,人们可能会发现这更容易理解(例如,0.6 的最佳表示为分数的总和。){进一步说明:您可能不是故意的,但您的评论“似乎有些 SO'ers 没有心情完全阅读这个问题......”听起来有点刺耳。纠正误解很好,但要礼貌。}
  • 给我看一个 V 和 r 的例子以及解集。
  • 一个例子是将0.70表示为集合{0.500, 0.250, 0.125}中数字的总和。贪心算法(如我删除的答案)给出0.500 + 0.125 = 0.625,其错误为0.075。更好的解决方案是0.500 + 0.250 = 0.750,错误为0.050

标签: c++ algorithm dynamic-programming


【解决方案1】:

算法

考虑一个目标数r 和一组F 的分数{1/2, 1/4, ... 1/(2^N)}。让最小的分数1/(2^N) 表示为P

那么最优和将等于:

S = P * round(r/P)

也就是说,最佳和 S 将是可用的最小分数 P 的一些 整数 倍数。最大错误err = r - S± 1/2 * 1/(2^N)。没有更好的解决方案,因为这需要使用小于1/(2^N) 的数字,这是集合F 中的最小数字。

由于分数F 都是P = 1/(2^N) 的2 的倍数,因此P 的任何整数倍 可以表示为F 中分数的总和。要获得应使用的分数列表,请将整数round(r/P) 编码为二进制,并在kth 二进制位置读取1 为“在解决方案中包含kth 分数”。

示例:

r = 0.3F{1/2, 1/4, 1/8, 1/16, 1/32}

  1. 将整个问题乘以 32。

    r = 9.6F{16, 8, 4, 2, 1}

  2. r 舍入到最接近的整数。

    r = 10.

  3. 10 编码为二进制整数(五位)

    10 = 0b 0 1 0 1 0    ( 8 + 2 )
            ^ ^ ^ ^ ^
            | | | | |
            | | | | 1
            | | | 2
            | | 4
            | 8
            16
    
  4. 将每个二进制位与分数相关联。

       = 0b 0 1 0 1 0    ( 1/4 + 1/16 = 0.3125 )
            ^ ^ ^ ^ ^
            | | | | |
            | | | | 1/32
            | | | 1/16
            | | 1/8
            | 1/4
            1/2
    

证明

考虑通过将所有涉及的数字乘以2**N 来转换问题,以便所有分数都变为整数。

原来的问题:

考虑0 &lt; r &lt; 1 范围内的目标数字r 和分数列表{1/2, 1/4, .... 1/(2**N)。找到总和为S 的分数列表的子集,使得error = r - S 最小化。

变成如下等价问题(乘以2**N后):

考虑0 &lt; r &lt; 2**N 范围内的目标数字r整数 {2**(N-1), 2**(N-2), ... , 4, 2, 1} 的列表。找到整数列表的子集,其总和为 S,使得 error = r - S 最小化。

选择总和为给定数字的 2 的幂(误差尽可能小)只是整数的二进制编码。因此这个问题归结为整数的二进制编码。

  • 存在解决方案:任何正浮点数r0 &lt; r &lt; 2**N,都可以转换为整数并以二进制形式表示。
  • 最优性:整数版本的最大误差是±0.5 的舍入误差。 (原题中,最大错误为±0.5 * 1/2**N。)
  • 唯一性:对于任何正(浮点)数,都有唯一的整数表示,因此也有唯一的二进制表示。 (0.5 的可能例外 = 见下文。)

实现(Python)

此函数将问题转换为等效整数,将r 舍入为整数,然后将r 的二进制表示形式读取为整数以获得所需的分数。

def conv_frac (r,N):
    # Convert to equivalent integer problem.
    R = r * 2**N
    S = int(round(R))

    # Convert integer S to N-bit binary representation (i.e. a character string
    # of 1's and 0's.) Note use of [2:] to trim leading '0b' and zfill() to
    # zero-pad to required length.
    bin_S = bin(S)[2:].zfill(N)

    nums = list()
    for index, bit in enumerate(bin_S):
        k = index + 1
        if bit == '1':
            print "%i : 1/%i or %f" % (index, 2**k, 1.0/(2**k))
            nums.append(1.0/(2**k))
    S = sum(nums)
    e = r - S

    print """
    Original number        `r` : %f
    Number of fractions    `N` : %i (smallest fraction 1/%i)
    Sum of fractions       `S` : %f
    Error                  `e` : %f
    """ % (r,N,2**N,S,e)

样本输出:

>>> conv_frac(0.3141,10)
1 : 1/4 or 0.250000
3 : 1/16 or 0.062500
8 : 1/512 or 0.001953

    Original number        `r` : 0.314100
    Number of fractions    `N` : 10 (smallest fraction 1/1024)
    Sum of fractions       `S` : 0.314453
    Error                  `e` : -0.000353

>>> conv_frac(0.30,5)
1 : 1/4 or 0.250000
3 : 1/16 or 0.062500

    Original number        `r` : 0.300000
    Number of fractions    `N` : 5 (smallest fraction 1/32)
    Sum of fractions       `S` : 0.312500
    Error                  `e` : -0.012500

附录:0.5 问题

如果r * 2**N0.5 结尾,则可以向上或向下舍入。也就是说,分数之和有两种可能的表示形式。

如果与原始问题陈述一样,您想要使用最少分数的表示(即二进制表示中1 位的最少数量),只需尝试两种舍入选项并选择更经济的选项。

【讨论】:

  • 我真的很想知道为什么当它找到一个据我所见似乎正确的解决方案时,为什么会被否决。
  • @MatthieuM。 :公平地说,我首先发布了一个快速而肮脏的答案,并将其编辑到您在此处看到的详细帖子中。我认为反对票来自早期版本。
  • 我想这是让投票者留下便条的另一个原因:这样他们就可以在进行编辑时收到通知......另一方面,即使它是正确的,它也很长-缠绕的解决方案。
  • @MatthieuM。 : 我想覆盖我所有的基地。当您深入了解它时,我最初的想法更像是 Marwin 的非常快速和肮脏的解决方案(明确地读取 r 的浮点表示中的位模式),但我没有勇气去写那个。 ;)
【解决方案2】:

也许我很笨……

我在这里看到的唯一技巧是(1/2)^(i+1)[0..n) 中的i 的总和,其中n 趋于无穷大,得到1。这个简单的事实证明,[i+1, n) 中的j,无论n 是什么,(1/2)^i 总是优于sum (1/2)^j

因此,在寻找我们的索引时,我们似乎没有太多选择。让我们从i = 0开始

  • r 中的任何一个都优于 2^-(i+1),因此我们需要
  • 或者它是次等的,我们需要选择[i+2, N]中的j2^-(i+1)还是sum 2^-j最接近(在相等的情况下推迟到后者)

唯一可能代价高昂的步骤是获取总和,但它可以一劳永逸地预先计算(甚至懒惰地预先计算)。

// The resulting vector contains at index i the sum of 2^-j for j in [i+1, N]
// and is padded with one 0 to get the same length as `v`
static std::vector<double> partialSums(std::vector<double> const& v) {
    std::vector<double> result;

    // When summing doubles, we need to start with the smaller ones
    // because of the precision of representations...

    double sum = 0;
    BOOST_REVERSE_FOREACH(double d, v) {
        sum += d;
        result.push_back(sum);
    }

    result.pop_back(); // there is a +1 offset in the indexes of the result

    std::reverse(result.begin(), result.end());

    result.push_back(0); // pad the vector to have the same length as `v`

    return result;   
}

// The resulting vector contains the indexes elected
static std::vector<size_t> getIndexesImpl(std::vector<double> const& v,
                                          std::vector<double> const& ps,
                                          double r)
{
  std::vector<size_t> indexes;

  for (size_t i = 0, max = v.size(); i != max; ++i) {
      if (r >= v[i]) {
          r -= v[i];
          indexes.push_back(i);
          continue;
      }

      // We favor the closest to 0 in case of equality
      // which is the sum of the tail as per the theorem above.
      if (std::fabs(r - v[i]) < std::fabs(r - ps[i])) {
          indexes.push_back(i);
          return indexes;
      }
  }

  return indexes;
}

std::vector<size_t> getIndexes(std::vector<double>& v, double r) {
    std::vector<double> const ps = partialSums(v);
    return getIndexesImpl(v, ps, r);
}

代码在ideone 运行(带有一些调试输出)。请注意,对于 0.3,它给出:

0.3:
   1: 0.25
   3: 0.0625
=> 0.3125

这与其他答案略有不同。

【讨论】:

    【解决方案3】:

    冒着被否决的风险,这个问题似乎相当简单。只需从V 中可以产生的最大和最小数字开始,依次调整每个索引,直到获得两个可能最接近的答案。然后评估哪个是更好的答案。

    这是未经测试的代码(使用我不写的语言):

    void getIndexes(std::vector<double>& V, double r)
    {
      double v_lower = 0;
      double v_upper = 1.0 - 0.5**V.size();
      std::vector<int> index_lower;
      std::vector<int> index_upper;
    
      if (v_upper <= r)
      {
        // The answer is trivial.
        for (int i = 0; i < V.size(); i++)
          cout << i;
        return;
      }
    
      for (int i = 0; i < N; i++)
      {
        if (v_lower + V[i] <= r)
        {
          v_lower += V[i];
          index_lower.push_back(i);
        }
    
        if (r <= v_upper - V[i])
          v_upper -= V[i];
        else
          index_upper.push_back(i);
      }
    
      if (r - v_lower < v_upper - r)
        printIndexes(index_lower);
      else if (v_upper - r < r - v_lower)
        printIndexes(index_upper);
      else if (v_upper.size() < v_lower.size())
        printIndexes(index_upper);
      else
        printIndexes(index_lower);
    }
    
    void printIndexes(std::vector<int>& ind)
    {
      for (int i = 0; i < ind.size(); i++)
      {
        cout << ind[i];
      }
    }
    

    我得到这份工作了吗! :D

    (请注意,这是一个可怕的代码,它依赖于我们确切地知道 V 在其中包含什么......)

    【讨论】:

      【解决方案4】:

      我首先要说我确实相信这个问题是微不足道的......

      (等到所有的石头都扔完)

      是的,我确实阅读了 OP 的编辑,该编辑说如果我这么认为,我必须重新阅读这个问题。因此,我可能会遗漏一些我看不到的东西 - 在这种情况下,请原谅我的无知并随时指出我的错误。

      我不认为这是一个动态编程问题。冒着听起来幼稚的风险,为什么不尝试在搜索索引时保留r 的两个估计值——即低估和高估。毕竟,如果r 不等于可以从V 的元素中计算出的任何总和,那么它将介于这两种总和之间。我们的目标是找到这些总和并报告更接近r

      我拼凑了一些快速而肮脏的 Python 代码来完成这项工作。它报告的答案对于 OP 提供的两个测试用例是正确的。请注意,如果 return 的结构必须始终返回至少一个索引 - 即使最佳估计根本没有索引。

      def estimate(V, r):
        lb = 0               # under-estimation (lower-bound)
        lbList = []
        ub = 1 - 0.5**len(V) # over-estimation = sum of all elements of V
        ubList = range(len(V))
      
        # calculate closest under-estimation and over-estimation
        for i in range(len(V)):
          if r == lb + V[i]:
            return (lbList + [i], lb + V[i])
          elif r == ub:
            return (ubList, ub)
          elif r > lb + V[i]:
            lb += V[i]
            lbList += [i]
          elif lb + V[i] < ub:
            ub = lb + V[i]
            ubList = lbList + [i]
        return (ubList, ub) if ub - r < r - lb else (lbList, lb) if lb != 0 else ([len(V) - 1], V[len(V) - 1])
      
      # populate V
      N = 5 # number of elements
      V = []
      for i in range(1, N + 1):
        V += [0.5**i]
      
      # test
      r = 0.484375 # this value is equidistant from both under- and over-estimation
      print "r:", r
      estimate = estimate(V, r)
      print "Indices:", estimate[0]
      print "Estimate:", estimate[1]
      

      注意:写完我的答案后,我注意到this answer 遵循相同的逻辑。唉!

      【讨论】:

      • 我认为您可能需要“从上面估计”的唯一情况是最佳解决方案的最低有效位不同(也就是说,数字应该被“四舍五入” “四舍五入”。)您可能只需执行一次贪心算法(“从下方”),然后手动检查“稍大”的解决方案是否给出更接近的结果。
      • @Li-aungYip 正如我所提到的,这只是我拼凑的一些快速代码。它可以通过多种方式进行优化。但它仍然只通过V 数组(或者这不是您所说的“通过贪心算法”的意思吗?)。
      【解决方案5】:

      不知道你有没有测试用例,试试下面的代码。这是一种动态规划方法。

      1] exp: given 1/2^i, find the largest i as exp. Eg. 1/32 returns 5.
      2] max: 10^exp where exp=i.
      3] create an array of size max+1 to hold all possible sums of the elements of V.
         Actually the array holds the indexes, since that's what you want.
      4] dynamically compute the sums (all invalids remain null)
      5] the last while loop finds the nearest correct answer.
      

      代码如下:

      public class Subset {
      
      public static List<Integer> subsetSum(double[] V, double r) {
          int exp = exponent(V);
          int max = (int) Math.pow(10, exp);
          //list to hold all possible sums of the elements in V
          List<Integer> indexes[] = new ArrayList[max + 1];
          indexes[0] = new ArrayList();//base case
          //dynamically compute the sums
          for (int x=0; x<V.length; x++) {
              int u = (int) (max*V[x]);
              for(int i=max; i>=u; i--) if(null != indexes[i-u]) {
                  List<Integer> tmp = new ArrayList<Integer>(indexes[i - u]);
                  tmp.add(x);
                  indexes[i] = tmp;
              }
          }
         //find the best answer
          int i = (int)(max*r);
          int j=i;
          while(null == indexes[i] && null == indexes[j]) {
              i--;j++;
          }
            return indexes[i]==null || indexes[i].isEmpty()?indexes[j]:indexes[i];
      }// subsetSum
      
      private static int exponent(double[] V) {
          double d = V[V.length-1];
          int i = (int) (1/d);
          String s = Integer.toString(i,2);
          return s.length()-1;
      }// summation
      
      public static void main(String[] args) {
          double[] V = {1/2.,1/4.,1/8.,1/16.,1/32.};
          double r = 0.6, s=0.3,t=0.256652;
          System.out.println(subsetSum(V,r));//[0, 3, 4]
          System.out.println(subsetSum(V,s));//[1, 3]
          System.out.println(subsetSum(V,t));//[1]
      }
      }// class
      

      以下是运行代码的结果:

      For 0.600000  get 0.593750 => [0, 3, 4]
      For 0.300000  get 0.312500 => [1, 3]
      For 0.256652  get 0.250000 => [1]
      For 0.700000  get 0.687500 => [0, 2, 3]
      For 0.710000  get 0.718750 => [0, 2, 3, 4]
      

      【讨论】:

      • 有趣的是,我们没有为0.3 找到相同的答案(我找到{1, 3} = 0.3125 (+0.0125) 你找到{1, 4} = 0.28125 (-0.01875))。这是一个错字还是你的代码从来没有从上面近似?
      • 尝试运行代码。我提供了一些额外的测试结果。
      • 那是一个错字:) 至于运行,我不知道它是用哪种奇怪的语言编写的,但无论如何我都觉得有点冗长。
      • 所以你不认识Java,面向对象编程的主要内容;你发现我的算法很冗长,尽管它的行数比你的少(除非你以某种方式在测试段和我提供的解释中混为一谈);而且您似乎不知道如何(即打扰)阅读您没有编写的代码。算法很简单:1]创建一个数组来保存结果; 2]动态计算每个总和(尽可能快); 3] 找到最接近r 的总和。在子集和研究中,这是分区方法,非常准确。
      • 实际上,我是在对 Java 的冗长和……我对这种语言缺乏热情进行双关语。我对您的“尝试运行代码”做出了反应:我不能,我没有 Java 编译器,如果我能提供帮助,我也不打算拥有一个。我没有评论它似乎很好用的算法,但是我对算法不太了解,所以我无法观察它的复杂性。最后......这不是代码高尔夫;)
      【解决方案6】:

      解决方案实现Polynomial time approximate algorithm。该程序的输出与其他解决方案的输出相同。

      #include <math.h>                                                                                                                                             
      #include <stdio.h>                                                                                                                                            
      #include <vector>                                                                                                                                             
      #include <algorithm>                                                                                                                                          
      #include <functional>                                                                                                                                         
      
      void populate(std::vector<double> &vec, int count)                                                                                                            
      {                                                                                                                                                             
          double val = .5;                                                                                                                                          
          vec.clear();                                                                                                                                              
          for (int i = 0; i < count; i++) {                                                                                                                         
              vec.push_back(val);                                                                                                                                   
              val *= .5;                                                                                                                                            
          }                                                                                                                                                         
      }                                                                                                                                                             
      
      void remove_values_with_large_error(const std::vector<double> &vec, std::vector<double> &res, double r, double max_error)                                     
      {                                                                                                                                                             
          std::vector<double>::const_iterator iter;                                                                                                                 
          double min_err, err;                                                                                                                                      
      
          min_err = 1.0;                                                                                                                                            
          for (iter = vec.begin(); iter != vec.end(); ++iter) {                                                                                                     
              err = fabs(*iter - r);                                                                                                                                
              if (err < max_error) {                                                                                                                                
                  res.push_back(*iter);                                                                                                                             
              }                                                                                                                                                     
              min_err = std::min(err, min_err);                                                                                                                     
          }                                                                                                                                                         
      }
      
      void find_partial_sums(const std::vector<double> &vec, std::vector<double> &res, double r)                                                                    
      {                                                                                                                                                             
          std::vector<double> svec, tvec, uvec;                                                                                                                     
          std::vector<double>::const_iterator iter;                                                                                                                 
          int step = 0;                                                                                                                                             
      
          svec.push_back(0.);                                                                                                                                       
          for (iter = vec.begin(); iter != vec.end(); ++iter) {                                                                                                     
              step++;                                                                                                                                               
              printf("step %d, svec.size() %d\n", step, svec.size());                                                                                               
              tvec.clear();                                                                                                                                         
              std::transform(svec.begin(), svec.end(), back_inserter(tvec),                                                                                         
                             std::bind2nd(std::plus<double>(), *iter));                                                                                             
              uvec.clear();                                                                                                                                         
              uvec.insert(uvec.end(), svec.begin(), svec.end());                                                                                                    
              uvec.insert(uvec.end(), tvec.begin(), tvec.end());                                                                                                    
              sort(uvec.begin(), uvec.end());                                                                                                                       
              uvec.erase(unique(uvec.begin(), uvec.end()), uvec.end());                                                                                             
      
              svec.clear();                                                                                                                                         
              remove_values_with_large_error(uvec, svec, r, *iter * 4);                                                                                             
          }                                                                                                                                                         
      
          sort(svec.begin(), svec.end());                                                                                                                           
          svec.erase(unique(svec.begin(), svec.end()), svec.end());                                                                                                 
      
          res.clear();                                                                                                                                              
          res.insert(res.end(), svec.begin(), svec.end());                                                                                                          
      } 
      
      double find_closest_value(const std::vector<double> &sums, double r)                                                                                          
      {                                                                                                                                                             
          std::vector<double>::const_iterator iter;                                                                                                                 
          double min_err, res, err;                                                                                                                                 
      
          min_err = fabs(sums.front() - r);                                                                                                                         
          res = sums.front();                                                                                                                                       
      
          for (iter = sums.begin(); iter != sums.end(); ++iter) {                                                                                                   
              err = fabs(*iter - r);                                                                                                                                
              if (err < min_err) {                                                                                                                                  
                  min_err = err;                                                                                                                                    
                  res = *iter;                                                                                                                                      
              }                                                                                                                                                     
          }                                                                                                                                                         
          printf("found value %lf with err %lf\n", res, min_err);                                                                                                   
          return res;                                                                                                                                               
      }                                                                                                                                                             
      
      void print_indexes(const std::vector<double> &vec, double value)                                                                                              
      {                                                                                                                                                             
          std::vector<double>::const_iterator iter;                                                                                                                 
          int index = 0;                                                                                                                                            
      
          printf("indexes: [");                                                                                                                                     
          for (iter = vec.begin(); iter != vec.end(); ++iter, ++index) {                                                                                            
              if (value >= *iter) {                                                                                                                                  
                  printf("%d, ", index);                                                                                                                            
                  value -= *iter;                                                                                                                                   
              }                                                                                                                                                     
          }                                                                                                                                                         
          printf("]\n");                                                                                                                                            
      }                                                                                                                                                             
      
      int main(int argc, char **argv)                                                                                                                               
      {                                                                                                                                                             
          std::vector<double> vec, sums;                                                                                                                            
          double r = .7;                                                                                                                                            
          int n = 5;                                                                                                                                                
          double value;                                                                                                                                             
          populate(vec, n);                                                                                                                                         
          find_partial_sums(vec, sums, r);                                                                                                                          
          value = find_closest_value(sums, r);                                                                                                                      
          print_indexes(vec, value);                                                                                                                                
          return 0;                                                                                                                                                 
      }
      

      【讨论】:

        【解决方案7】:

        对向量进行排序并搜索最接近 r 的分数。存储该索引,从 r 中减去该值,然后用 r 的余数重复。迭代直到达到 r,否则找不到这样的索引。

        例子:

        0.3 - 可用的最大值为 0.25。 (索引 2)。现在的余数是 0.05

        0.05 - 可用的最大值为 0.03125 - 余数为 0.01875

        等等

        等等。每一步都是在排序数组中进行 O(logN) 搜索。步数也将是 O(logN) 总复杂度将超过 O(logN^2)。

        【讨论】:

        • 会有实际解决方案大于 r 的情况,您的提案没有考虑这些解决方案。当 r = 0.3 且 N 为 30 时的示例就是这种情况的示例。顺便说一句,我没有 -1,但我不会接受你的回答。
        • 哦,最接近的也可以更大。可以轻松调整。
        • 正确读题,例如N=11,r=0.3,解为:1/4,1/32,1/64,1/512,1/1024,1/2048 ,你的建议不起作用,不要假设你知道的类似的东西可以在这里应用,这不是一个简单的二分问题。
        • @WeaselFox:你为什么要对 vector 进行排序?你的方法看起来像是某种划分。从 n = 0 开始,如果(1/2)^n 不如目标,则存储n,从数量中删除(1/2)^n,递增n,继续...我看不出“排序”步骤来自哪里来自。
        • @XanderTulip 对于 N = 11,r = 0.3,解决方案不包括 1/2048。如果包含,则错误为 0.00029296875,否则为 0.0001953125。
        【解决方案8】:

        这不是动态规划问题

        输出应该是整数(索引)向量,而不是双精度向量

        这可能是 0-2 的精确值,这只是概念:

        A) 输出零索引,直到 r0(r - 已输出的索引值)大于 1/2

        B) 检查 r0 double 的内部表示并:

        x (第一个位移) = -指数; // 指数越大,数字越小(以 1/2^(x) 开头的 x 越大)

        用body检查循环中float小数部分的位表示: (方向取决于小/大端)

        {
          if (bit is 1)
            output index x;
          x++;
        

        }

        每一步的复杂度是恒定的,所以总体来说是 O(n),其中 n 是输出的大小。

        【讨论】:

        • 提供的赞成的解决方案只是我算法的不太有效的版本。
        • 这基本上是建议查看 IEEE 754 浮点数的二进制表示吗?也就是说,列出设置了尾数的哪些位,并针对指数进行调整?
        • Li-aung Yip@ 是的,这样一来,您一方面可以获得精确和快速的结果,另一方面可能会失去便携性。简单的方法答案已经存在,所以我只想提供替代方法
        • 它应该可以在任何使用 IEEE 754 浮点的地方工作。对我来说足够便携!
        【解决方案9】:

        解释这个问题,r 的二进制表示中的一位(在二进制点之后)是什么? N 是“精度”,如果你愿意的话。

        在 Cish 伪代码中

        for (int i=0; i<N; i++) {
          if (r>V[i]) {
            print(i);
            r -= V[i];
          }
        }
        

        您可以为 r == 0 添加额外的测试以提前终止循环。

        请注意,这给出了最接近“r”的二进制数,即如果有两个同样“正确”的答案,则该二进制数更接近于零。

        如果第 N 个数字是 1,您需要将“1”添加到获得的“二进制”数字中,并与原始“r”进行核对。 (提示:构造向量 a[N], b[N] of 'bits',设置 '1' 位而不是上面的 'print'。设置 b = a 并手动添加,从 ' 末尾逐位b' 直到你停止携带。转换为 double 并选择更接近的那个。

        注意 a[]

        '最少数量的索引 [原文如此]' 是一个红鲱鱼。

        【讨论】:

        • 在某些情况下,总和将大于 r,但对于给定的 2 倒数的 N 次方,差值将是所有其他可能总和中最小的。因此您的解决方案无效。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-16
        • 2023-03-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多