ただ繰り返さない
色んな問題に触れていると、全く思いつかないというのは減ってきた気がしますが
それでもまだまだとりあえずループしてゴリ押し出力!というのが少なくありません。。
解けた後にもっとスリムにやれないか??と思うこともあるので、今回はそんなスリム化の過程を書いてみたいと思います!
1~入力値までの数を足していく
1~入力値まで足していく間に "+" 最後に "="を入れて出力する というプログラムを考えたいと思います。
入力値が5であれば、 1+2+3+4+5=15
と表示するような流れです。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int answer = 0;
for(int i = 1; i <= n; i++) {
// 最後は+を入れたくないので分岐させる
if(i == n){
System.out.print(i);
} else {
System.out.print(i + "+");
answer += i;
}
}
System.out.println("=" + answer);
}
}
一応これでも問題なく出力はできるんですが、最後の1回だけ+を入れないため、というたった1回しかないケースのためだけにif文があるので、繰り返しの度に条件判定を行うのはかなり無駄っぽいです。。
最後の数を表すi == n
のときだけを特別扱いしてあげれば良さそうなので改良してみます。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int answer = 0;
for(int i = 1; i < n; i++) {
//入力値 - 1 の数まで足す
System.out.print(i + "+");
answer +=i;
}
//最後の入力値自身は別で足す
System.out.println(n + "=" + (answer +=n));
}
}
最後だけは別で足せばいいので、出力するときにanswer +=n
で足すようにしました。
これで無駄な条件分岐はなくなり、見た目も処理そのものもかなり効率的になったと思います!
入力値文 +- を交互に出力する
表題の通りですが、3であれば +-+ 5であれば +-+-+ と表示するプログラムを考えます。
交互なので、奇数であれば - 偶数であれば + という分岐を考えれば良さそうです。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
for(int i = 1; i <= n; i++){
//偶数であれば - を出力する
if(i % 2 == 0){
System.out.print('-');
}else{
System.out.print('+');
}
}
}
}
ただ、これもfor文の中で条件分岐を行っているので処理回数が増えてしまいそうなので、条件分岐を外に出してみましょう。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
//まずは入力値を2で割った数分だけ+-を出力する
for(int i = 0; i < n / 2; i++){
System.out.print("+-");
}
//奇数であれば最後に +を足す
if(n % 2 != 0){
System.out.println('+');
}
}
}
偶数個分の+-をまずは出力してしまうために、for(int i = 0; i < n / 2; i++)
としました。
その後、もし入力値が奇数であれば最後に+だけを足すことで for文とif文を分けることができましたね!
奇数と偶数は結構考え方として大事そうですね。。
*をn回表示し、 w個並んだら改行を入れる
こちらも表題の通りのプログラムですが、2つの入力値があるとして、1番目をn、2番目をwとしたときに
*をn回表示し、 w個並んだら改行を入れて出力するというプログラムを試してみます。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int w = sc.nextInt();
for (int i = 1; i <= n; i++) {
System.out.print('*');
// 表示する順番が改行を入れる個数の倍数であった場合は改行を入れる
if(i % w == 0){
System.out.println();
}
}
}
}
30 5
と入力されたときのことを考えてみると、 30個の*を出力するけど、5個出力する毎に改行を入れる。
ということになるので、「5,10,15,20,25,30」 というタイミングで改行を入れる必要がありそうです。
整理してみると、、
i番目に出力するとき、iがwの倍数であるときは、出力後に改行を入れる必要がある。
→ iをwで割り切れるとき、出力後に改行を入れればいいということになります。
これをfor (int i = 1; i <= n; i++)
で判定するとにより実現しています。。。
「forの中でifはもうナンセンスゴリラじゃない??」 という声が聞こえてきそうなので、改良してみましょう笑
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int w = sc.nextInt();
for (int i = 0; i < n / w; i++) {
System.out.println("*".repeat(w));
}
int rest = n % w;
if(rest != 0){
System.out.print("*".repeat(rest));
}
}
}
繰り返し出力をStringクラスのrepeatメソッドを使用して行い、
改行は for (int i = 0; i < n / w; i++)
の n / w
で繰り返す行数を決めています。
行数 = 改行込みで繰り返し出力する数(ループが必要な数)ということですね!
もし割り切れない場合は、改行は必要ないけど出力が必要な回数が残っているので
n % w
で求めた余り分だけを再度出力することによって、繰り返し処理と条件分岐を分けることができました!
学んだこと
- 繰り返し文の中に条件分岐がある場合は、別々に分けられないか考えてみる。
- 条件分岐が必要な場面がどれくらい発生するかを考えてみる。
- 奇数と偶数で分けられるときは、単純化できるチャンス
これまではとりあえず期待値が出ればよしとしていましたが、扱うデータの数が何千、何万となってくれば
それだけ処理が重たくなってしまうので、for文とif文は分けるなど、できる限り見た目も処理方法もスマートにできないか考える癖を付けていこうと思います! 引き続き頑張っていきます!