【问题标题】:How to condense my 9 if statements into one [closed]如何将我的 9 个 if 语句浓缩为一个 [关闭]
【发布时间】:2018-10-10 22:34:33
【问题描述】:

我想看看能被所有一位数整除的最小数字是多少,我没有查找它,而是创建了这个。

public static void main(String[] args) {

    for (int i = 100; i < 10000; i++) {

        if (i % 2 ==0) {

            if (i % 3 ==0) {

                if (i % 4 ==0) {

                    if (i % 5 ==0) {

                        if (i % 6 ==0) {

                            if (i % 7 ==0) {

                                if (i % 8 ==0) {

                                    if (i % 9 ==0) {

                                        System.out.println(i);

                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

如您所见,我在 if 语句 x9 中有一个 if 语句。该代码有效,但我想使用数组压缩我的 if 语句以使我的 if 语句像这样,但它不起作用。

 if (i % x[1, 2, 3, 4, 5, 6, 7, 8]) {
 System.out.println(i);
 break;
 }

有什么建议吗?

【问题讨论】:

  • 使用&amp;&amp; ?或创建一个通用方法,如isModuloOf(int number, int...modulosToTest)
  • 从字面上看,if (i%2==0 &amp;&amp; i%3==0 /* etc */) 将是一个 if 语句。
  • 查看埃拉托色尼筛法,了解解决此问题的巧妙算法。 en.m.wikipedia.org/wiki/Sieve_of_Eratosthenes
  • 如果i % 8 == 0 那么检查i % 4 == 0i % 2 == 0 是没用的。 i % 3 == 0 i % 9 == 0 时相同
  • 这个问题更适合codereview.stackexchange.com

标签: java


【解决方案1】:

更简单的方法:

    public static boolean isDivisible(int number) {
        for (int i = 2; i <= 9; i++) {
            if (num % i != 0) {
                return false;
            }
        }
        return true;
    }

并且使用相同类型的结构,main方法变成:

    public static void main(String[] args) {
        for (int i = 100; i <= 100000; i++) {
            if (isDivisible(i)) {
                System.out.println("Divisible by numbers 2...9: " + i);
                break;
            }
        }
    }

【讨论】:

    【解决方案2】:

    我想你可以使用LCM (Least Common Multiple) of (1, 2, 3, 4, 5, 6, 7, 8, 9) = 2520,像这样:

    if (i % 2520 == 0) {
        System.out.println(i);
        break;
    }
    

    【讨论】:

    • 9!不等于 2520,不知道你从哪里得到这个数字。
    • @jbch 这是最小公倍数,而不是总倍数。如果某物可以被 8 整除,那么它也可以被 4 和 2 整除。
    • 如果可用,使用 LCM 函数来计算这个2520,而不是硬编码它。这既可以作为有关此幻数是什么的文档,也可以在将来轻松更改。如果没有可用的 LCM 功能并且您不想为其引入新的依赖项,请至少在右侧写评论// LCM(1, 2, 3, 4, 5, 6, 7, 8, 9) = 2520
    • @bigsandwich 是的,我明白,答案已被编辑。在答案中有等式1*2*3*4*5*6*7*8*9=2520之前,这显然是错误的。 LCM 正是他们试图在不查找的情况下计算的内容。他们没有明确说明,但可被 A、B 和 C 整除的最小数 A、B 和 C 的 LCM,因此对 LCM 进行硬编码或使用 LCM 函数会破坏这一点练习。
    【解决方案3】:

    你为什么不..

    反转 IF?


    public static void main(String[] args) {
    
        for (int i = 100; i < 10000; i++) {
    
            //If value is not valid, continue to next value
            if (i % 2 != 0) continue;
            if (i % 3 != 0) continue;
            if (i % 4 != 0) continue;
            if (i % 5 != 0) continue;
            if (i % 6 != 0) continue;
            if (i % 7 != 0) continue;
            if (i % 8 != 0) continue;
            if (i % 9 != 0) continue;
    
            //Valid value found. Print and break out of the loop.
            System.out.println(i);
            break;
        }
    }
    

    或者,上面的代码可以进一步重构为:

    public static void main(String[] args) {
        for (int i = 100; i < 10000; i++) {
            if (isPrintable(i)) {
                System.out.println(i);
                break;
            }
        }
    }
    
    private static boolean isPrintable(int value) {
        return value % 2 == 0
               && value % 3 == 0
               && value % 4 == 0
               && value % 5 == 0
               && value % 6 == 0
               && value % 7 == 0
               && value % 8 == 0
               && value % 9 == 0;
    }
    

    此外,根据@TeePeemm 的建议,isPrintable() 可以简化为:

    private static boolean isPrintable(int value) {
        for (int divisor = 2; divisor < 10; divisor++) {
            if (value % divisor != 0) return false;
        }
        return true;
    }
    

    1.正如其他答案中所建议的那样,也有基于语言的快捷方式。我同意他们的看法。

    2.许多答案使用数字的 LCM 来使代码简洁,但这是一个等待咬人的休眠错误。循环执行完全改变,可以通过注释掉break; 看到。看似简单的解决方案引入了一个微妙的潜在错误。

    【讨论】:

    • 基本上是 goto :P。但如果条件太复杂而无法将它们放入数组并循环遍历它们,它实际上非常有用。 +1。
    • @Frax:你为什么称它为 goto 我的朋友?在我看来,我只是预先验证值,几乎就像我们在方法顶部验证外部输入的方式一样(比如检查字符串是否为空、空或空格以及输入数字是否为正,等等。)。我现在编辑了答案并添加了一个验证方法。你仍然以同样的方式看待我的解决方案吗?
    • 我没有恶意!只是,至少在这种规模下使用,这是一种非常命令式的风格,基于,嗯,跳过代码的某些部分。没有错,它很好而且惯用。实际上,我一直在编写这样的代码。它非常适合这个问题。
    • 从纯粹基于逻辑/数学的方法中,您可以从 8 个模测试减少到 4 个,因为其中一些(或它们的组合)暗示其他较低的数字:9(也称为3) ,8(也是4,也是298也是6),7(素数)和5(素数)。当然,实际输出只是这 4 个数字的乘积...
    • 我喜欢这个,因为它不是代码问题的数学答案,在性能方面花费花生,如果问题复杂化,不容易出现隐藏的逻辑/数学错误,并且足够明确的未来- 六个月后你将不需要阅读 cmets 来弄清楚你做了什么。
    【解决方案4】:

    好吧,这可能不是最好的方法,但我想尝试使用任意一组数字。

    public interface Divisor {
        boolean isDivisible(Long value);
    }
    
    public static main(String[] args) {
        LCMDivisor divisor = LCMDivisor.init()
                .addValue(2L)
                .addValue(3L)
                .addValue(4L)
                .addValue(5L)
                .addValue(6L)
                .addValue(7L)
                .addValue(8L)
                .addValue(9L);
    
        LongStream.range(1, 10000)
                .filter(divisor::isDivisible)
                .findFirst()
                .ifPresent(System.out::println);
    }
    

    所以我们创建了一个对象,Divisor,它有一个方法可以告诉你一个值是否可以被它自己整除。

    代码通过创建从 1 到 N 的 Longs 流来运行,过滤掉所有不能被除数整除的值,然后取第一个值(根据您的“break”语句)。它返回一个可选项。如果存在值,则该值将打印到标准输出。

    对于这个例子,根据上面的 cmets,我介绍了一个 Divisor 的实现,它存储所有添加的值的最小公倍数。初始化时,它的值为 1;但是每次添加一个新值时,它都会返回一个具有最小公倍数的除数的新实例。实现如下所示:

    public class LCMDivisor implements Divisor {
    
        public final Long lcmValue;
    
        private LCMDivisor(Long lcmValue) {
            this.lcmValue = lcmValue;
        }
    
        public static LCMDivisor init() {
    
            return new LCMDivisor(1L);
        }
    
        public Boolean isDivisible(final Long value) {
            return value % lcmValue == 0;
        }
    
        public LCMDivisor addValue(final Long newValue) {
    
            return new LCMDivisor(lcm(newValue));
        }
    
        private Long lcm(final Long newValue) {
            return newValue * (lcmValue / gcd(newValue));
        }
    
        private Long gcd(final Long newValue) {
    
            Long greater = newValue < lcmValue ? lcmValue : newValue;
            Long lesser = newValue > lcmValue ? lcmValue : newValue;
    
            while (lesser > 0)
            {
                long temp = lesser;
                lesser = greater % lesser;
                greater = temp;
            }
            return greater;
        }
    }
    

    【讨论】:

    • LCM 方法较差,但您的解决方案设计令人印象深刻。
    • @displayName,为什么差?我喜欢这种方法,因为它不必为它测试的每个数字都经过每个模。我们的两种方法都在 N 的数量级上,但是您的方法必须对每个测试值进行模计算,而这种方法在前面做了一些密集的工作,然后减少了后端的工作。
    • 见我回答的第二个脚注。
    • 我看到上面的解决方案很快就失败了,但它仍然可以,至少有一半的情况下会做不止一个模运算。该解决方案最多只能对每个检查值进行一次模运算。 (顺便说一句,享受讨论:))
    • Gerald Weinberg 讲述了一位程序员飞往底特律帮助调试有问题的程序的故事。程序员与开发程序的团队一起工作,几天后得出结论,情况已经没有希望了。在回家的航班上,他仔细考虑了情况并意识到问题所在。飞行结束时,他有了新代码的大纲。他对代码进行了几天的测试,正要返回底特律时,收到了一封电报,说该项目已被取消,因为程序无法编写。 (1/3)
    【解决方案5】:

    您基本上在做的是试图找到数字i,它是2 * 3 * 4 * 5 * 6 * 7 * 8 * 9LCM。最初,您可能只想简单地写

    if (i % (2 * 3 * 4 * 5 * 6 * 7 * 8 * 9) == 0) {
        System.out.println(i);
        break;
    }
    

    如果所有数字都是互质的,那将是真的。这意味着他们没有任何共同因素。但是在这种情况下,这些数字不是互质的并且具有公因数。就像8 = 2*2*2, 4 = 2*2, 6 = 2*3 都有 2。3 = 1 * 3 , 6 = 2*3, 9 = 3*9 都有 3。所以基本上我们必须取 LCM 的数字 2,3,4,5,6,7,8,9。请参阅以下编辑以更正上述公式。

    数字2,3,4,5,6,7,8,9 的 LCM(最小公倍数)是 = 2520。 所以通过所有测试用例的正确公式如下

    if ( i % 2520 == 0) { 
      System.out.println(i); 
      break;
    }
    

    另一个使用的解决方案是简单地检查所有条件,如下所示:

    if(i % 9 == 0 && i % 8 ==0 && i % 7 == 0 && i % 5 == 0) {
       System.out.println(i);
       break;
    }
    

    【讨论】:

    • OP 不检查该数字是否可以被1*2*3*4*5*6*7*8*9 整除。请编辑答案,只留下真实的陈述。不需要包括原始的。
    • 这个答案的前提是错误的。问题是要求编写代码来找到最小公倍数,而不是检查一个数字是否可以被最小公​​倍数整除。
    • 问题是“我想看看能被所有一位数字整除的最小数字是多少”——这相当于说“我想找到数字 2-9 的 LCM”。然后问题的作者说“我没有查找它,而是创建了这个函数” - 换句话说,问题假设 2-9 的 LCM 是未知的。如果循环从 2521 开始,代码将找不到 LCM,因此无法实现其声明的目的。
    【解决方案6】:

    试试这个。

    for (int i = 100; i < 10000; ++i) {
        int x = i;
        if (IntStream.of(2, 3, 4, 5, 6, 7, 8, 9).allMatch(k -> x % k == 0)) {
            System.out.println(i);
            break;
        }
    }
    
    -> 2520
    

    或者你可以把它写成一个语句。

    int result = IntStream
        .range(100, 10000)
        .filter(i -> IntStream.of(2, 3, 4, 5, 6, 7, 8, 9).allMatch(k -> i % k == 0))
        .findFirst()
        .getAsInt();
    
    System.out.println(result);
    
    -> 2520
    

    【讨论】:

    • 这将是一个更好的答案,如果它解释了人们如何想出这段代码以及代码在做什么。
    • 我最喜欢这个答案,因为它不依赖于检查多个模数的具体情况。
    • @DavidZ 序列处理机制现在有望成为常识。 Java 的流只是 Python 的列表解析和 .NET 的 LINQ 的最新成员之一。
    • @jpmc26 这可能是真的,但这并不意味着添加解释不会改善答案。
    【解决方案7】:

    如果您尝试使用从 1 到 20 或 1 到 30 的数字,您的循环将花费很长时间。您可以直接计算 least common multiple

    package stackOverflow;
    import java.util.stream.LongStream;
    
    public class NumberTheory
    {
    
        public static void main(String[] args) {
            System.out.println(gcd(15, 3) == 3);
            System.out.println(gcd(13, 11) == 1);
            System.out.println(gcd(60, 24) == 12);
            System.out.println(gcd(1071, 462) == 21);
            System.out.println(gcd(462, 1071) == 21);
            System.out.println(gcd(new long[] { 10, 12, 24, 60 }) == 2);
            long[] oneToNine = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            System.out.println(gcd(oneToNine) == 1);
            System.out.println(lcm(oneToNine));
            long[] oneToTwenty = LongStream.range(1, 21).toArray();
            System.out.println(lcm(oneToTwenty));
        }
    
        /**
         * Calculates the greatest common divisor of 2 numbers which are not all zero. (see
         * https://en.wikipedia.org/wiki/Greatest_common_divisor)
         * 
         * Recursive version of Euclidean algorithm (https://en.wikipedia.org/wiki/Euclidean_algorithm)
         * 
         * @param m
         * @param n
         * @return greatest common divisor of m and n
         */
        public static long gcd(long m, long n) {
            if (m == 0 || n == 0) {
                return m + n;
            } else {
                return gcd(n, m % n);
            }
        }
    
        /**
         * Calculates the greatest common divisor of n numbers. The array should have at least one number which isn't zero.
         * 
         * @param numbers
         * @return greatest common divisor of numbers
         */
        public static long gcd(long[] numbers) {
            long result = numbers[0];
            for (int i = 1; i < numbers.length; i++) {
                result = gcd(result, numbers[i]);
            }
            return result;
        }
    
        /**
         * Calculates the least common multiple of 2 numbers which are both non zero. see
         * https://en.wikipedia.org/wiki/Least_common_multiple
         * 
         * @param m
         * @param n
         * @return least common multiple of m and n
         */
        public static long lcm(long m, long n) {
            return m * (n / gcd(m, n));
        }
    
        /**
         * Calculates the least common multiple of n numbers. The array should have at least one number and shouldn't contain
         * any zero.
         * 
         * @param numbers
         * @return least common multiple of numbers
         */
        public static long lcm(long[] numbers) {
            long result = numbers[0];
            for (int i = 1; i < numbers.length; i++) {
                result = lcm(result, numbers[i]);
            }
            return result;
        }
    }
    

    它输出:

    true
    true
    true
    true
    true
    true
    true
    2520
    232792560
    

    【讨论】:

      【解决方案8】:

      虽然这可能不是最理想的,但它确实会浓缩您的陈述:

      public static void main(String[] args)
      {
          int remainder;
          for (int i = 100; i < 10000; i++) 
          { 
             remainder=0;
             for (int j=2; j<10; j++) 
                 remainder+=i % j; 
             if (remainder == 0)
                  System.out.println(i);
         }
      }
      

      对于每个 i,我们使用内部循环 j 将其与从 2 到 9 的每个数字取模。我们将每个取模结果添加到余数变量中。

      在内循环结束时,只有当这个 i 的所有模数都为零时,余数仍然为零。

      【讨论】:

      • 似乎被混淆了。当j&lt;=9 更清晰时,你为什么要做j&lt;10?使用括号将有助于阅读代码。如果您解释为什么remainder+=i % j 有效,那将会有所帮助。那条线花了我一点时间。
      • @mackycheese21 我添加了解释。我对循环变量使用与 OP 相同的约定。而且我不认为代码被混淆了。这只是实现结果的另一种方式,在一些更复杂的情况下可以避免昂贵和/或冗长的语句。
      • 伟大的编辑。但是在for(int j=2;j&lt;19;j++) 中包含错误的数字条件不是更容易吗?
      • @mackycheese21 用于未知或非常长的循环 - 是的。对于只有 10 次运行的短循环,我们避免了每个循环最多 8 个条件。我想我无意识地试图避免分支预测:stackoverflow.com/questions/315306/is-if-expensive 这在这种情况下并不重要。
      【解决方案9】:

      实际上有两个问题:第一部分是,如何将 9 个条件相似的 if 语句压缩成更易读的形式。另一个可能是无意的问题是,应该如何将“一位数的 LCM”之类的内容添加到代码中。让我们从后者开始,下面进入前者。

      谷歌搜索结果

      如果你的程序需要这种数字(而不是程序计算它的唯一目的),你应该通过最简单的必要手段获得它(在这种情况下,@987654321 @),并将其作为常量包含在您的程序中,可能还带有一些关于数字来自何处的注释。

      如果您不能直接找到它,请尝试自己计算它(就像 rgettman 所做的那样),然后再次将其包含为常量。如果失败或花费太多时间,请编写一次性程序来计算数字,但不要使用常量将其作为更大程序的一部分。不过,将一次性代码存储在某处 是个好主意。评论可能是正确的地方。

      遍历数组

      现在是关于压缩 if 语句的内容。

      有一些使用流的解决方案,但在您的情况下,一个简单的数组可能会更好。此代码也更通用,您可以轻松地将其移植到几乎任何语言,并且它与数字没有任何关联(您可以使用任何东西的数组)。奖励点 - 任何人都应该理解。

      static boolean divisibleByAll(int n, int[] divisors) {
          for (int d : divisors) {
              if (n % d != 0) {
                  return false;
              }
          }
          return true;
      }
      
      static int lcmOfSingleDigits() {
          int[] divisors = {1, 2, 3, 4, 5, 6, 7, 8, 9};
          for (int i = 100; i < 10000; i++) {
              if (divisibleByAll(i, divisors)) {
                  return i;
              }
          }
          return -1;  // Perhaps better to throw an exception
      }
      
      public static void main(String args[]) {
          System.out.println("Smallest number divisible by all one digit numbers: " +
                             lcmOfSingleDigits());
      }
      

      使用流

      大多数 Java-ish 解决方案,这就是您应该在实践中使用的解决方案 - 除非您需要非 Java 程序员来阅读您的代码。 saka1029 和 pkpnd 的回答都涵盖了,不再赘述。

      【讨论】:

        【解决方案10】:

        正如其他几个人所提到的,您正在尝试做的是计算数字 1、2、3、...、9 的 least common multiple

        但是您如何在计算机中做到这一点?首先,您需要知道如何计算两个数字的greatest common divisor

        function gcd2(a, b)
            while b ≠ 0
                t := b; 
                b := a mod b; 
                a := t; 
            return a;
        

        现在,两个数的最小公倍数可以通过一个简单的公式从它们的最大公约数中计算出来:

        function lcm2(a, b)
            if a = 0 and b = 0
                return 0;
            else
                return abs(a*b) / gcd2(a,b);
        

        (a 和 b 都为零的特殊情况是避免被零除的必要条件。)

        最后,LCM(a,b,c) = LCM(LCM(a,b),c),所以要计算两个以上数字的 LCM,迭代一个列表:

        function lcmN(ns)
            let rv := 1;
            for n in ns
                rv := lcm2(rv, n);
            return rv;
        

        将伪代码翻译成 Java 留作练习。

        【讨论】:

          【解决方案11】:

          从 Java 8 开始,您可以使用Stream 方法(特别是使用IntStream)。

          首先,我们使用IntStream.rangeClosed(2, 9)(或等效的IntStream.range(2, 10))来获取以2 开头并以9(含)结尾的连续Integers 流。我们可以通过使用.allMatch(...) 将此流转换为boolean,当且仅当每个流元素 匹配某些条件时,它才会返回true。所需标准以lambda expressionn -&gt; i % n == 0 的形式提供。这可以写成(Integer n) -&gt; (i % n == 0),因此 lambda 表达式将来自名为n 的流中的Integer 作为输入,并返回i(循环计数器)是否可以被n 整除。因此,如果 i 可以被流中的每个 Integer 整除,.allMatch(n -&gt; i % n == 0) 将返回 true

          我们需要再做一个修改:lambda 表达式中使用的变量(例如i)必须实际上是final

          一个变量或参数,其值在初始化后永远不会改变,它实际上是最终的。 (Oracle documentation)

          但是,循环计数器i不是实际上是最终的,因为它每次迭代都会递增(因此重新分配)。解决方案是在循环内声明一个新变量int x = i;,这样x 在其范围内只分配一次(即循环的一次迭代)。因此,x实际上是最终的,可以在 lambda 表达式中使用。

          这是最终的解决方案:

          import java.util.stream.IntStream;
          
          public static void main(String[] args) {
              for (int i = 100; i < 10000; i++) {
                  int x = i; // x is effectively final
                  if (IntStream.rangeClosed(2, 9).allMatch(n -> x % n == 0)) {
                      System.out.println(i);
                      break;
                  }
              }
          }
          

          【讨论】:

            【解决方案12】:

            起初您会认为可以通过将 2 到 9 的乘积放在 % 运算符的右侧来一次测试所有这些。

            if (i % (2 * 3 * 4 * 5 * 6 * 7 * 8 * 9) == 0)
            

            但由于某些数字在其因式分解中包含先前的数字,因此您应该使用较小的数字,特别是最小公倍数。 8 是 2 和 4 的倍数,9 是 3 的倍数,如果乘积中有 8 和 9,那么 6 (2 * 3) 也包括在内。

            if (i % (5 * 7 * 8 * 9) == 0)
            

            结果是 2520,这是最小公倍数。使用2520 并在评论中解释为什么使用这个数字会更具可读性。

            /**
             * The goal is to test if the number is a multiple of all integers
             * from 2 through 9.  Mathematically, the least common multiple to is a
             * multiple of all its input numbers.  Here, the LCM of 2, 3, ..., 9 is 2520.
             */
            public static final int LCM_2_THRU_9 = 2520;
            

            我已经声明了一个常量,我将在这里使用它:

            if (i % LCM_2_THRU_9 == 0)
            

            【讨论】:

            • 既然5 * 7 * 8 * 9 无论如何都会混淆原始逻辑,不妨写2520 然后评论它是2, 3, 4, 5, 6, 7, 8, 9Least Common Multiple。跨度>
            • 建议:声明一个常量 lcm 并添加注释,它是 {2, 3, 4, 5, 6, 7, 8, 9} 的最小公倍数。与编译器相同,人类更容易理解。
            • 你有点违背了练习的重点。现在您正在寻找可被 2520 整除的最小数字(0 除外)...剧透,它是 2520。但为了得到数字 2520,您必须首先进行计算。
            • @gman 但那是错误的!做if (x % 2 &amp;&amp; x % 4)if (x % 4) 相同,与if (x % 8) 不同。乘法仅适用于数字不能被彼此整除的情况,例如 23
            • 您不妨建议将代码替换为 System.out.println(2520);
            【解决方案13】:

            正如之前回答的那样,编写您正在尝试做的事情的最简洁的方法可能是检查 2 到 9 的乘积。

            但是,要回答您关于如何压缩 if 语句的问题;嵌套的 if 语句等价于逻辑运算符 AND,因此您也可以按以下方式编写 if 语句:

            if (i % 2 == 0 && i % 3 == 0 && i % 4 == 0 && i % 5 == 0 && i % 6 == 0 && i % 7 == 0 && i % 8 == 0 && i % 9 == 0) {
            System.out.println(i);
            }
            

            【讨论】:

            • 您可以消除较早的因素,方法与优化埃拉托色尼筛网消除较晚的倍数相同。任何能被 8 整除的东西也能被 4 和 2 整除。但一个完整的答案会保持短路。如果从偶数开始,建议步长为 2。
            • @mckenzm:这不是一个好建议。请参阅我的回答中的脚注 2。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-09-07
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多