简体   繁体   中英

Print numbers in specific range without using any loop or conditions (Java)

Maybe first idea come to mind to solve this kind of problems is recursive function but also it will be a challenge to write recursive function without any condition.

I tried this approach to print numbers from 10 to 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);
}   

But it will crash when it reach number 61 without exception handling

also even with try catch the Arithmetic Exception it's still not a preferable solution because it's handling an exception ( runtime error ).

I think main problem when using recursive functions is the stop condition.

As well I read that there is a way in C++ by creating a class with a static variable counter and initialize it then increment the counter variable and print it in the constructor after that instantiating number of objects of class counter will print these numbers.

Any suggested solutions to solve this challenge would be appreciated.

You can do something like this: (Idea taken from this 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();}



}

Based on @Imposter's answer, a reduce version with compacted but readable code

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 "";
    }
}

Here is a solution using a hashmap, a bitwise operator, an equality expression and reflection:

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);
  }
}

Ok so that is one way to do it and here is the more OO way where you don't use the extra stuff.

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);
  }
}

You can use semaphores to limit the recursion count:

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 class launches a watcher thread to terminate the process after counting is complete.

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);
    }
}

Your program will crash because at 61 int divisonByZero = 1 / (61 - n); will become int divisonByZero = 1 / 0; Which is a division by zero and raises an exception. int divisonByZero = 1 / 0; Which is a division by zero and raises an exception.

You can use a try-catch to catch the exception but I don't know if you see this as a condition. It's also bad practice to use an exception for this. But below you find how you would implement such a version.

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]));
   }
}

Why not to use simple stream?

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

Stream is not a loop, but you can say "kind of". In your case with division by zero - you can wrap the division statement in try block. And exit without an error.

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

Am I allowed to use implicit numeric conversions?

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 );
    }
}

The only defect is that a large range will cause a StackOverflowError because of too many (tail-)recursive calls.

Since all loops require conditional statements we can simplify the issue to "list an arbitrary range of numbers without conditionals." In java there is no boolean -> integer conversion, bools can only be used in conditionals so we can cross that off of the list.

That leaves us with the arithmetic option. We can fake boolean values with 1's and 0's. To use the 1 or 0 instead of true/false, we could make an array of 2 methods, the first would be our recursive code path and the other be our stop. So all we would need is an algorithm that with a count of 0 returns 1, and a count of any non zero value returns 0.

There are a lot of things that separate 0 from all other numbers, but it is hard to take advantage of without dividing by zero. The property I took advantage of is that 0 sits between the positive and negative numbers. If we abs the set of whole numbers, zero is the only number that has the same number on both sides (1 and 1). Knowing that, we know that abs(n-1/n+1) will be 1 for n=0 and <1 for all other positive number. If we cast the result to an int, it will drop any decimal and leave us with g(0)=1 and g(any positive number)=0. Notice that the n+1 is the denominator for if we had n-1 on the bottom an of 1 would divide by zero.

To avoid any external code we can replace abs(n) with n*n since n can only be between -1 and 1. And that is it, ((a-1)/(a+1)) * ((a-1)/(a+1)) sure looks wacky but it perfectly does what we want.

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);
}

At first, why would you need to resolve any such problem? What prohibits you from the standard cycles and even the standard "IF"? ...This sounds to me like just a scholar hypothetical discuss. :-/

Anyway:

As mentioned already, every repeatable sequence needs an "IF", the condition to stop: It definitely will appear on the processor (imagine the ASM instruction), during the runtime. The only difference is, on which level of abstraction/architecture the IF is caused:

  • Either directly within the code (as the basic level, however prohibited here, in the question) ..no matter, whether syntactically real IF , or by the ternary ?: ,
  • ...
  • or ie in the JVM inheritance mechanism , on the other edge-extreme of the spectrum of possibilities: The polymorphism does the IF, but internally, implicitly. (Did anyone mention that here yet?) I imagine two mutated-object classes, implementing the same method:

    • In one implementation, the altered method, there would be a hard-stop,
    • in the other just a recursion-call, of the "runtime class".

    Such methods would be pretty short and straightforward.

    ...these could be implementations of an abstract class : It's up to you.

Still, whichever solution-implementation would not change the fact: The IF is still there, somewhere.

Using an Exception for normal program flow is poor form, but this works. Eventually there will be a division by zero exception which is the signal to quit.

public class App {

    public static void main(String[] args) {
        App app = new App();
        try {
            app.print(10, 60);
        } catch (ArithmeticException ae) {
            // ignore
        }
    }

    private void print(int next, int until) {
        System.out.println(next);
        assertNotEndOfRange(next, until);
        print(++next, until);
    }

    private int assertNotEndOfRange(int next, int until) {
        return 0 / (until - next);
    }

}

Your code

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);
}

You said that the program crashes when it reaches 61.

It is a runtime error

More specifically ArithmeticException

How?

You have this condition

int divisionByZero = 1 / (61-n);

when n reaches 61, then 1 / (61 - 61)

which is equal to 1 / 0 which is an error.

To stop this, you have to implement try...catch to catch arithmetic exceptions

So, the code will be

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);
}

I don't think it is even theoretically doable. Either way you have to have a stop-condition. It can be:

  • an if
  • a return with a ternary operator
  • an exception check (internally does instanceof )
  • a lambda (a lot of solutions exist, most of them will probably involve one-or-more implicit loops and one-or-more implicit if s)

If you go to the deepest level humanly comprehensible (aka assembly), you will definitely have at least one jmp in the resulting program. This fact is independent of the higher-level language you use.

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));
    }

}

You can use Java 8 streams as follows:

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

The result:

10
11
12
.
.
.
58
59

I have three solutions without a loop in or condition in the Sourcecode

The first one uses the JavaScript Engine to evaluate a string command which is stored in a byte[] at compile-time.

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));
    }
}

The second one writes a .class-File to the Homedirectory an executes it.

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);
    }
}

The third one uses the getOrDefault -Method of a Map have something like a condition:

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) {}
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM