【问题标题】:optimize code to get the number of integers within given range that are divisible by integer优化代码以获取给定范围内可被整数整除的整数个数
【发布时间】:2012-08-04 00:45:55
【问题描述】:

给定范围 x, y。我需要计算两者之间的所有数字并且可以被 n 整除。

我知道做到这一点的简单方法是循环整个范围

for(int i=x;i<=y;i++) if(i%n ==0) counter++; 

计数器保存答案。

但这在大范围内工作得非常慢。例如 x=0 和 y=3,000,000,000。

我确信有一些关系可以用来减少迭代次数并优化此代码以提高速度。我搜索了但我找不到它。谁能帮帮我。

非常感谢。

【问题讨论】:

    标签: java c++ math division


    【解决方案1】:

    这有效:(e+1 - s) / d + (e%d &lt; (s+d-1)%d)。 (它使用 C 语义和整数运算,并假设开始是非负的。s 是开始值,e 是结束值 [包括],d 是除数。)

    更新:更好的解决方案是e/d - (s-1)/d。这是受 User448810 启发的。这要求 s 是积极的;处理零或负 s(或 e)需要调整向零的截断(对于这个问题,我们希望向负无穷大)。

    负值更新:以下适用于 s 和 e 在其类型范围内的任何值,前提是 s

    e = e <  0 ? (e+1)/d - 1 : e/d;
    s = s <= 0 ? s/d - 1 : (s-1)/d;
    return e-s;
    

    基本上,前两个语句等价于 e = e/ds = (s-1)/d,除法被修改为向 -infinity 舍入而不是向零舍入(因此 -1/10 产生 -1 而不是 0)。

    【讨论】:

    • 你能解释一下吗?你是怎么到达那里的?
    • 这与@Peter 的解决方案基本相同,只是这个解决方案适当地考虑了端点选择的差异。
    • @EricPostpischil ,这不是家庭作业。这是一道面试题,我也是在网上找到的l
    • 当您通过整数计数时,余数以除数 d 为模,逐步遍历余数 0、1、2、... d-3、d-2、d-1,然后循环,再次从 0 开始。从 s 到 e,这些循环有一些整数(可能为零)。整个循环的数量是(e+1 - s)/d,每个完整的循环恰好包含一个余数为零的数字(这意味着该数字是 d 的倍数)。所以我们知道整个周期有那么多倍数,我们只需要弄清楚剩下的部分周期是零还是一。 [在下一条评论中继续。]
    • 我们可以假设我们已经取出了整个循环,并且只考虑从 s 到 e 的部分循环。如果在从 s 到 e 的过程中,我们“绕过”残差,从 d-1 到 0,那么在部分循环中有一个倍数。否则,没有。起初,我们可能只是考虑一下:e (e%d) 的余数是否小于 s 的余数?如果是这样,我们就四处走动,因为这就是您从高残留到低残留的方式。但是,我们还必须考虑 s 为零的情况,在这种情况下,我们从倍数开始,不需要绕行。 [继续。]
    【解决方案2】:
    (floor)(high/d) - (floor)(low/d) - (high%d==0)
    

    说明:

    从 0 到 a 有可被 d 整除的 a/d 数。 (d!=0)

    因此 (floor)(high/d) - (floor)(low/d) 将给出可在 (low,high] 范围内整除的数字(请注意,不包括低,而高包括在此范围内)

    现在要从范围中删除高位,只需减去 (high%d==0)

    对浮点数使用 fmodf。

    【讨论】:

      【解决方案3】:

      只有这个实现对我有用([0..2kkk] 中的 A、B;[1..2kkk] 中的 K):

      function solution(A, B, K) {
          if(B < K) return A === 0 ? 1 : 0;
          if(A === B) return A % K === 0 ? 1 : 0;
      
          var pre = A === 0 ? 2 : 1;
          var min = A < K ? K : A + A % K;
          var max = B - B % K;
      
          return (max - min) / K + pre;
      }
      

      【讨论】:

      • 您可以在这里用各种语言测试它或您自己的:www.codility.com/demo/take-sample-test/count_div
      【解决方案4】:

      您可以尝试,而不是遍历每个数字,

      public int test(int floor, int ceil, int n) {
          int a = (floor / n) + 1;
          int counter = 0;
          while (a * n <= ceil) {
              counter++;
              a++;
          }
          return counter;
      }
      

      并且只使用除数的倍数。现在您不是在重复除法(慢),而是在重复乘法(更快)。

      【讨论】:

      • 别忘了关闭硬件预取器 :)
      • 从 O(n) 到 O(1/n) 并用乘法代替除法都是一个巨大的改进。
      • 非常感谢您的贡献。对于庞大的数字,它仍然很慢
      • 输入:x=0,y=Integer.MAX_VALUE,n=Integer.MAX_VALUE
      • 对于 (1, 1, 2),这将返回 2,但答案显然为零。可能您的意思是 a * d &lt;= y 而不是 a * x &lt;= ,但对于 (1, 1, 2) 仍然失败;它返回一个。
      【解决方案5】:

      我很确定你可以做到这一点:

      public static int numDivisible(int low, int high, int test) {
          return (high-low)/test;
      }
      

      给你。一个恒定时间的解决方案。由于您不需要确切知道哪些数是可整除的,因此您无需费心遍历所有数。

      编辑:根据@Chaser324 将其更改为以下内容。

      public static float numDivisible(float low, float high, float test) {
          return Math.ceil((high-low)/test);
      }
      

      编辑:一个小错字,即将text 更改为test

      【讨论】:

      • 不完全,尝试 range(3-10) 和 test = 3,你会得到 2,但他的答案是 3。你需要以某种方式确定要添加 1 的情况
      • 对于 (6, 10, 3),这将返回一((10-6)/3 产生 1),但显然区间包含三的两个倍数(六和九)。
      • 这个解决方案似乎可以工作,并且是迄今为止最快的,只需稍作修改。对浮点数的强制转换和对圆整的 ceil() 调用是否可行?我没有处理足够多的案例来看看这是否总是有效。
      • 新公式 ceil((high-low)/text) 为 (4, 4, 2) 生成零,即使 4 显然可以被 2 整除。
      • @Nate, @Eric Postpischil -- 我认为这确实需要一些修改。 @Coder Stacker -- 因为你不需要知道哪些数字可以被test 整除,我想你可以通过计算在该范围内有多少数字可以被test 整除。显然我一开始是不对的,但这个想法是可能的。
      【解决方案6】:

      您要求计算 x 和 y(x 和 y 都包含在范围内)之间可以被 n 整除的整数个数。不需要任何循环,只需要两个除法来计算答案。让我们考虑一个简单的例子:对于 100 到 200 的范围,有多少个整数可以被 7 整除?

      从范围的低端开始:100 / 7 = 14,余数为 2。现在除数 7 减去余数 2 为 5,因此可被 7 整除的最小数为 100 + 5 = 105。

      现在转到范围的高端:200 / 7 = 28,余数为 4,因此范围内可被 7 整除的最大数是 200 - 4 = 196。

      因此,范围内能被 7 整除的数字是 105、112、119、126、133、140、147、154、161、168、175、182、189 和 196。其中有 14 个,您可以通过几种方式计算。取两端的商并减去它们:28 - 14 = 14。或者取两个端点的差,除以除数,然后加1:196 - 105 = 91, 91 / 7 = 13, 13 + 1 = 14. 但是当端点之一可以被除数整除时要小心;我将把它留给你来解决细节,并编写程序。

      【讨论】:

      • 这很接近但有错误,因为它错误地说明了起点。简单地取最后的商并减去失败。例如,对于 (2, 2, 1),它产生 0 (2/2-2/2 = 0),但 1 是正确的。在后一种情况下,当端点之一为倍数时进行调整是不够的,因为存在两个端点都不是倍数但区间中有额外倍数的情况。
      【解决方案7】:

      请提供以下算法的cmets:假设范围为[R1,R2],待除数为n。

      从 R1 开始,找出能被 n 整除的最小数。称它为小数。

      从 R2 开始,找出能被 n 整除的最大数。称它为大。

      可整除的总数=(大-小)/n + 1。

      最坏的情况仍然是 O(n),但对于 R1 和 R2 之间的巨大差异可能是有效的。希望我已经涵盖了所有情况。请建议。

      int numofDivisible(int R1, int R2, int n) {
      
      int small = R1, large= R2;
      
      if ((R1 > R2) || (n==0)) return 0;
      
      if (R1 == R2) {
          if (R1 % n == 0) 
              return 1;
          else 
              return 0;
      }
      
      while(small <= R2){
      
           if(small % n == 0)
               break;
      
            ++small;
      }
      
      if (small > R2)
          return 0;
      
      
      while (large > small) {
      
          if (large % n == 0)
             break;
      
          --large;
      }
      
      if (large == small)
         return 1;
      
      return ((large-small)/n + 1);
      
      }
      

      【讨论】:

      • 它工作,不像这里的大多数答案,但它使用迭代来找到除数倍数开始和结束的区间内的点。正如我们在其他答案中看到的那样,迭代不是必需的。我们可以直接计算这些点。
      • 嗨,埃里克,感谢您的反馈。同意我们不需要使用迭代。在发布其他解决方案时,我正在发布我的解决方案。
      【解决方案8】:
      public static int solution(int low,int high, int n) {
          boolean h0=high%n==0;
          boolean l0=low%n==0;
      
          int k1=l0?low/n:(low+n-low%n)/n;
          int k2=h0?high/n:(high+n-high%n)/n;
      
          //                 |-----------|
          // ---------------k1----------k2---------------
      
          if(k2*n>high) k2--;
      
          return k2-k1+1;
      
      }
      

      【讨论】:

        【解决方案9】:
         public int myfunc(int low, int high, int test) {   
            int count = 0;
            if(low % test == 0)
               count++;
            count +=(high-low)/test;
            return count;
         }
        

        【讨论】:

          【解决方案10】:

          好吧,我不是大学教授,所以我没有给你一些惊人的公式,但据我所知,检查这样的数字列表的唯一方法是遍历它们,在最后,您将不得不评估变量以检查它是否可整,现在有一种方法可以优化您的算法,首先对数字进行排序!这最初会很昂贵,但任何后续需要检查数字都会快得多,

          我建议查看排序算法,我会使用冒泡排序,它会在 google 上出现很多。

          至于您可以对排序做什么,您可以将数字排序为倍数列表,例如 2 4 6 8 都是 2 的倍数,因此您基本上可以检查列表中的第一个,其余的将是立即知道也可整除

          请记住,这样做可能有更有效的方法,只需提供我的 2 美分

          【讨论】:

          • 您认为对数字进行排序有助于解决这个问题吗?
          • 你先排序数字,测试第一个元素。然后,每个下一个可分元素都是 current + N。类似的东西。从那里你可以得到这些数字,或者只计算而不用迭代:)
          • 抱歉,为了更准确,我实际上添加了更多内容
          • 我仍然不明白排序在哪里有用。最优化的算法根本不使用迭代,但迭代算法开始有一个排序集R1, R1+1, R1+2, ... , R2-2, R2-1, R2。你在分类什么?
          • 我一定误解了 OP 试图做什么,我的印象是他有一个数字列表,他需要通过对该列表进行排序来检查可被数字整除的范围进入可被数字整除的子列表,他可以检查列表中的第一个,并且已经知道其余的数字也是可整除的。我通读了这篇文章,但根本不明白,我也说过“请记住,这样做可能有更有效的方法”
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-09-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多