在不使用任何循环或条件的情况下打印特定范围内的数字(Java)

时间:2017-06-06 20:18:54

标签: java loops recursion

也许首先想到解决这类问题的想法是递归函数,但是在没有任何条件的情况下编写递归函数也是一个挑战。

我尝试过这种方法来打印10到60之间的数字:

public static void printNumbers(int n){
       int divisonByZero = 1 / (61 - n);
       System.out.println(n);
       printNumbers(n+1);
}     
public static void main(String[] args) {
       printNumbers(10);
}   

但是当它在没有异常处理的情况下达到61时会崩溃

即使尝试捕获算术异常,它仍然不是一个更好的解决方案,因为它处理异常(运行时错误)。

我认为使用递归函数时的主要问题是停止条件。

我还读到C ++中有一种方法是通过创建一个带有静态变量计数器的类并初始化它然后递增计数器变量并在构造函数中打印它之后,类计数器的实例化对象将打印这些数字。

任何建议的解决方案都可以解决这一挑战。

16 个答案:

答案 0 :(得分:6)

你可以这样做:(从这个answer获取的想法)

public class Application {

    public static void main(String[] args) {
        Print40Numbers();
        Print10Numbers();

    }

    private static int currentNumber = 10;

    private static void Print1Number() { System.out.println(currentNumber++); }
    private static void Print2Numbers() { Print1Number(); Print1Number(); }    
    private static void Print5Numbers() { Print2Numbers(); Print2Numbers(); Print1Number(); }   
    private static void Print10Numbers() { Print5Numbers();Print5Numbers();}
    private static void Print20Numbers() { Print10Numbers();Print10Numbers();}
    private static void Print40Numbers() { Print20Numbers();Print20Numbers();}



}

答案 1 :(得分:5)

基于@ Imposter的答案,具有压缩但可读代码的缩减版本

class Sandbox
{
    public static void main(String args[]) throws Exception
    {
        System.out.println(getfalse(10, 60));
    }

    public static String getfalse(Integer start, Integer stop) throws Exception
    {
        return
            start + "\n" +
            Sandbox.class.getMethod("get" + (start == stop), Integer.class, Integer.class)
            .invoke(Sandbox.class, start+1, stop);
    }

    public static String gettrue(Integer start, Integer stop)
    {
        return "";
    }
}

答案 2 :(得分:4)

以下是使用散列映射,按位运算符,等式表达式和反射的解决方案:

import java.lang.reflect.*;
import java.util.*;

public class Range
{

  private final Map<Boolean, Integer> truth;

  Range()
  {
    truth = new HashMap<>();
    truth.put(true, 0);
    truth.put(false, 1);
  }

  public void printRange(int start, int stop) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
  {
    print1(start, stop);
  }

  public void print1(Integer start, Integer stop) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
  {
    int quit = start ^ stop;
    int method = truth.get(quit == 0);
    System.out.println(start);

    String whichMethod = Integer.toString(method);
    Method toCall = this.getClass().getMethod("print" + whichMethod, Integer.class, Integer.class);
    toCall.invoke(this, start + 1, stop);
  }

  public void print0(Integer start, Integer stop)
  {
    System.exit(0);
  }

  public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
  {
    Range range = new Range();
    range.printRange(-10, 60);
  }
}

好的,这是一种方法,这里有更多的OO方式,你不使用额外的东西。

import java.util.*;

public class Range
{

  interface Printer
  {
    void print(int start, int end);
  }

  private final Map<Boolean, Printer> truth;

  Range()
  {
    truth = new HashMap<>();
    truth.put(true, new Quit());
    truth.put(false, new KeepGoing());
  }

  public void printRange(int start, int stop)
  {
    truth.get(false).print(start, stop);
  }

  private class KeepGoing implements Printer
  {
    public void print(int start, int stop)
    {
      int quit = start ^ stop;
      Printer method = truth.get(quit == 0);
      System.out.println(start);

      method.print(start + 1, stop);
    }
  }

  private class Quit implements Printer
  {
    public void print(int start, int stop)
    {
      return;
    }
  }

  public static void main(String[] args)
  {
    Range range = new Range();
    range.printRange(-10, 60);
  }
}

答案 3 :(得分:4)

您可以使用信号量来限制递归计数:

import java.util.concurrent.Semaphore;

public class PrintNumbers extends Thread 
{
  static int start = 10;
  static int end = 60;

  static Semaphore available = new Semaphore(end - start, true);
  static Semaphore completed = new Semaphore(end - start, true);

  public static void main(String[] args) throws Exception {
    completed.drainPermits();  
    (new PrintNumbers()).start(); //Start watcher thread
    counter(start);
  }

  // Recursive function for counting
  public static void counter(int count) throws Exception{
    System.out.println(count);
    count++;
    completed.release();
    available.acquire(); // Will stop here when there is no more to count
    counter(count);
  }  

  public void run() {
    completed.acquireUninterruptibly(end - start);
    System.exit(0);  // Terminate process
  }
}

PrintNumbers类在计数完成后启动一个观察者线程来终止进程。

答案 4 :(得分:4)

You can use java.util.BitSet which is a class that represents a large set of positive integers.

class Number {
    public static void main(String[] args) {
        int n = 100;
        String set = new java.util.BitSet() {{ set(1, n+1); }}.toString();
        System.out.append(set, 1, set.length()-1);
    }
}

答案 5 :(得分:3)

您的计划会崩溃,因为61 int divisonByZero = 1 / (61 - n);将成为int divisonByZero = 1 / 0; Which is a division by zero and raises an exception.

你可以使用try-catch来捕捉异常,但我不知道你是否认为这是一个条件。为此使用例外也是不好的做法。但是下面你会发现如何实现这样的版本。

public class Main {

   public static void printNumbers(int n, int stop) {
       System.out.println(n);
       try {
          int divisonByZero = 1 / (stop - n);
          printNumbers(n + 1, stop);
       } catch (ArithmeticException e) {
          System.out.println("program is terminated");
       }
   }

   public static void main(String[] args) {
       printNumbers(Integer.parseInt(args[0]), Integer.parseInt(args[1]));
   }
}

答案 6 :(得分:3)

为什么不使用简单的流?

IntStream.range(10, 60).forEach(System.out::println)

Stream不是循环,但你可以说“有点”。在除以零的情况下 - 您可以将除法语句包装在try块中。并且没有错误退出。

try { 
    int divisonByZero = 1 / (61 - n);
    ...
catch (ArithmeticException e) {
    // exit or return something to stop recursion
}

答案 7 :(得分:3)

我可以使用隐式数字转换吗?

public class NoLoopNoConditional {
    @SuppressWarnings( "unchecked" )
    private final Consumer< Integer >[] afn =
        ( Consumer< Integer >[] )new Consumer[ 2 ];
    private final double dDelta;
    private final int iLow;

    public NoLoopNoConditional( int iLow, int iHigh ) {
        this.iLow = iLow;
        this.dDelta = ( double )iHigh + 1 - iLow;
        // load the recursive and terminal cases
        afn[ 0 ] = ( i ) -> {};
        afn[ 1 ] = ( i ) -> {
            System.out.println( i );
            recur( i + 1 );
        };
    }

    // returns 1 until i exceeds iHigh, then returns 0
    private int choice( int i ) {
        return ( int )( ( dDelta + dDelta - i + iLow ) / ( dDelta + 1 ) );
    }

    private void recur( int i ) {
        afn[ choice( i ) ].accept( i );
    }

    public static void main( String[] args ) {
        // grab the parameters
        // throws exception if wrong # of args or can't parse. no conditionals
        int iLow = Integer.parseInt( args[ 0 ] ), iHigh = Integer.parseInt( args[ 1 ] );

        // go for it
        new NoLoopNoConditional( iLow, iHigh ).recur( iLow );
    }
}

唯一的缺点是,由于太多(尾部)递归调用,大范围会导致StackOverflowError

答案 8 :(得分:3)

由于所有循环都需要条件语句,我们可以将问题简化为“列出没有条件的任意数字范围”。在java中没有布尔值 - &gt;整数转换,bools只能在条件中使用,所以我们可以越过列表。

这给我们留下了算术选项。我们可以伪造1和0的布尔值。要使用1或0而不是true / false,我们可以创建一个包含2个方法的数组,第一个是我们的递归代码路径,另一个是我们的停止。所以我们需要的是一个算法,其计数为0,返回1,任何非零值的计数返回0。

有很多东西将0与其他所有数字分开,但很难在没有除以0的情况下利用它。我利用的属性是0位于正数和负数之间。如果我们对整数的集合进行abs,那么零是唯一一个在两边都有相同数字的数字(1和1)。知道了,我们知道abs(n-1 / n + 1)对于n = 0将是1并且对于所有其他正数而言将是&lt; 1。如果我们将结果转换为int,它将丢弃任何小数,并留下g(0)= 1和g(任何正数)= 0。请注意,n + 1是分母,如果我们在底部有n-1,则1的n将除以零。

为了避免任何外部代码,我们可以用n * n替换abs(n),因为n只能在-1和1之间。就是这样,((a-1)/(a + 1))*( (a-1)/(a + 1))肯定看起来很古怪,但它完全符合我们的要求。

interface doMethod {
    void doIt(int start, int stop);
}
private static doMethod[] doList = new doMethod[] {
        new doMethod() { public void doIt(int start, int stop) { printNumbers(start, stop); } },
        new doMethod() { public void doIt(int start, int stop) {}}
};
public static void printNumbers(int start, int stop){
    System.out.println(start);
    //a is our 'count' variable
    int a = stop - start;
    //b is our 'index' variable
    int b = ((a-1)/(a+1)) * ((a-1)/(a+1));
    // doList[0 = recurse, 1 = stopRecursing]
    doList[b].doIt(start + 1, stop);
}
public static void main(String[] args) {
    printNumbers(10, 60);
}

答案 9 :(得分:2)

首先,您为什么需要解决任何此类问题?什么禁止你从标准周期甚至标准“IF”? ......这听起来像是一个学者假设的讨论。 : - /

反正:

如上所述,每个可重复的序列都需要一个“IF”,即停止的条件:它肯定会在运行时出现在处理器上(想象ASM指令)。唯一的区别是IF引起的抽象/架构级别:

  • 直接在代码中(作为基本级别,但在此处禁止,在问题中)..无论如何,语法上是真实的IF,还是由三元?:
  • ...
  • 或者在JVM 继承机制中,在可能性的另一边 - 极端:多态执行IF,但内部,隐式。 (有人在这里提到过吗?)我想象两个变异对象类,实现相同的方法:

    • 在一个实现中,改变的方法,会有一个硬停,
    • 在另一个只是一个递归调用,“运行时类”。

    这些方法非常简短直接。

    ......这些可能是抽象类的实现:这取决于你。

但是,无论哪种解决方案实施都不会改变这一事实:IF仍然存在,某处。

答案 10 :(得分:1)

对正常的程序流使用异常是不好的形式,但这是有效的。最终将有一个除零异常,即退出的信号。

class LoginController < ApplicationController
  skip_before_action :verify_authenticity_token

  def new
    if session[:user]
      @user = User.find(session[:user])
    end
  end

  def destroy
    reset_session
    redirect_to "/login/acesso", notice: "Você foi deslogado"
  end


  def create
    user = User.validate(login_params[:email], login_params[:senha])
    if user
      session[:user] = user.id
      redirect_to "/home/index", notice: "login feito com sucesso"
    else
      redirect_to "/login/acesso", notice: "Dados incorretos"
    end

  end
  private
  def login_params
    params.require(:login).permit(:email, :senha)
  end
end

答案 11 :(得分:1)

您的代码

public static void printNumbers(int n){
       int divisonByZero = 1 / (61 - n);
                               ^^^(1/61-61) => 1/0 => Dividedbyzero error
       System.out.println(n);
       printNumbers(n+1);
}     
public static void main(String[] args) {
       printNumbers(10);
}

你说程序在达到61时崩溃了。

这是运行时错误

更具体地说ArithmeticException

如何?

你有这个条件

int divisionByZero = 1 / (61-n);

n达到61,然后1 / (61 - 61)

等于1 / 0,这是一个错误。

要停止此操作,您必须实现try...catch以捕获算术异常

所以,代码将是

public static void printNumbers(int n){
       try{
           int divisonByZero = 1 / (61 - n);
           System.out.println(n);
           printNumbers(n+1);
       }catch(ArithmeticException e){
       }
}     
public static void main(String[] args) {
       printNumbers(10);
}

答案 12 :(得分:1)

我认为它在理论上甚至不可行。无论哪种方式,你必须有一个停止条件。 它可以是:

  • if
  • 带有三元运算符的return
  • 异常检查(内部instanceof
  • 一个lambda(存在很多解决方案,其中大多数可能涉及一个或多个隐式循环和一个或多个隐式if

如果你达到人类可理解的最深层次(a.k.a. assembly),你将肯定在结果程序中至少有一个jmp。这个事实与您使用的高级语言无关。

答案 13 :(得分:1)

here is my code... got idea from imposter. Thanks @imposter

package com.test;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class StackOverFlow {
    Map<Integer, String> methodCall = new HashMap<>();
    static int diff;
    static int reminder;
    static int methodNumber;
    public static void print1(Integer start, Integer end) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        diff= (end.intValue()-1)-start.intValue();
        reminder = diff % 2;
        methodNumber = reminder+1;
           System.out.println(start.intValue());
           //System.out.println("methodNumber   " + methodNumber);
           Method call =StackOverFlow.class.getDeclaredMethod("print"+methodNumber,Integer.class,Integer.class);
           call.invoke(StackOverFlow.class, start.intValue()+1,end);

    }
    public static void print0(Integer start, Integer end){

           //System.out.println(n.intValue());
           System.exit(0);

    }
    public static void print2(Integer start, Integer end) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        diff= (end.intValue()-1)-start.intValue();
        reminder = diff % 2;
        methodNumber = reminder+1;

           System.out.println(start.intValue());
           //System.out.println("methodNumber   " + methodNumber);
           Method call =StackOverFlow.class.getDeclaredMethod("print"+methodNumber,Integer.class,Integer.class);
           call.invoke(StackOverFlow.class, start.intValue()+1,end);

    } 

    public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {

           print1(Integer.valueOf(10),Integer.valueOf(60));
    }

}

答案 14 :(得分:0)

您可以按如下方式使用Java 8流:

import java.util.stream.IntStream;
public class StreamApp {
     public static void main(String[] args) {
         IntStream.range(10, 60).forEach(System.out::println);
     }
}

结果:

10
11
12
.
.
.
58
59

答案 15 :(得分:-1)

我有三个解决方案没有源代码中的循环或条件

第一个使用JavaScript引擎来评估在编译时存储在byte []中的字符串命令。

import javax.script.ScriptEngineManager;
import java.nio.charset.StandardCharsets;


public class PrintNumbers
{
    public static void main(String... args) throws Exception
    {
        byte[] iCantSeeALoopHere = new byte[]{102, 111, 114, 32, 40, 105, 32, 61, 32, 49, 48, 59, 32, 105, 32, 60, 61, 32, 54, 48, 59, 32, 105, 43, 43, 41, 32, 123, 32, 112, 114, 105, 110, 116, 40, 105, 41, 59, 32, 125};
        new ScriptEngineManager().getEngineByName("JavaScript").eval(new String(iCantSeeALoopHere, StandardCharsets.UTF_8));
    }
}

第二个将.class-File写入Homedirectory并执行它。

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;


public class PrintNumbers
{
    public static void main(String... args) throws Exception
    {
        byte[] iCantSeeALoopHere = new byte[]{-54, -2, -70, -66, 0, 0, 0, 52, 0, 31, 10, 0, 5, 0, 17, 9, 0, 18, 0, 19, 10, 0, 20, 0, 21, 7, 0, 22, 7, 0, 23, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, 100, 101, 1, 0, 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 4, 109, 97, 105, 110, 1, 0, 22, 40, 91, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 86, 1, 0, 13, 83, 116, 97, 99, 107, 77, 97, 112, 84, 97, 98, 108, 101, 1, 0, 10, 69, 120, 99, 101, 112, 116, 105, 111, 110, 115, 7, 0, 24, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 0, 17, 80, 114, 105, 110, 116, 78, 117, 109, 98, 101, 114, 115, 46, 106, 97, 118, 97, 12, 0, 6, 0, 7, 7, 0, 25, 12, 0, 26, 0, 27, 7, 0, 28, 12, 0, 29, 0, 30, 1, 0, 12, 80, 114, 105, 110, 116, 78, 117, 109, 98, 101, 114, 115, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 1, 0, 19, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 69, 120, 99, 101, 112, 116, 105, 111, 110, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 121, 115, 116, 101, 109, 1, 0, 3, 111, 117, 116, 1, 0, 21, 76, 106, 97, 118, 97, 47, 105, 111, 47, 80, 114, 105, 110, 116, 83, 116, 114, 101, 97, 109, 59, 1, 0, 19, 106, 97, 118, 97, 47, 105, 111, 47, 80, 114, 105, 110, 116, 83, 116, 114, 101, 97, 109, 1, 0, 7, 112, 114, 105, 110, 116, 108, 110, 1, 0, 4, 40, 73, 41, 86, 0, 33, 0, 4, 0, 5, 0, 0, 0, 0, 0, 2, 0, 1, 0, 6, 0, 7, 0, 1, 0, 8, 0, 0, 0, 29, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 1, 0, 9, 0, 0, 0, 6, 0, 1, 0, 0, 0, 1, 0, -119, 0, 10, 0, 11, 0, 2, 0, 8, 0, 0, 0, 74, 0, 2, 0, 2, 0, 0, 0, 23, 16, 10, 60, 27, 16, 60, -93, 0, 16, -78, 0, 2, 27, -74, 0, 3, -124, 1, 1, -89, -1, -16, -79, 0, 0, 0, 2, 0, 9, 0, 0, 0, 18, 0, 4, 0, 0, 0, 5, 0, 9, 0, 7, 0, 16, 0, 5, 0, 22, 0, 9, 0, 12, 0, 0, 0, 9, 0, 2, -4, 0, 3, 1, -6, 0, 18, 0, 13, 0, 0, 0, 4, 0, 1, 0, 14, 0, 1, 0, 15, 0, 0, 0, 2, 0, 16};
        Path javaClassFile = Paths.get(System.getProperty("user.home"), "PrintNumbers.class").toAbsolutePath();
        Files.write(javaClassFile, iCantSeeALoopHere);

        new ProcessBuilder(
                "java",
                "-cp",
                javaClassFile.getParent().toString(),
                javaClassFile.getFileName().toString().replace(".class", "")
        ).inheritIO().start().waitFor();

        Files.delete(javaClassFile);
    }
}

第三个使用getOrDefault - 地图方法有条件:

import java.util.Map;
import java.util.HashMap;
import java.util.function.BiConsumer;

public class PrintNumbers
{
    private static Map<Integer, BiConsumer<Integer, Integer>> funcMap;

    public static void main(String[] args)
    {
      funcMap = new HashMap<>();
      funcMap.put(0, PrintNumbers::doNothing);

      printNumbers(10, 60);
    }

    private static void printNumbers(int curr, int end)
    {
      System.out.println(curr);

      BiConsumer<Integer, Integer> next = funcMap.getOrDefault(end - curr, PrintNumbers::printNumbers);
      next.accept(curr + 1, end);
    }

    private static void doNothing(int a, int b) {}
}