- 投稿日:2021-08-09T22:22:51+09:00
Rubyを用いた逆数和のコーディングテスト
某プログラミングスクールに通っていた転職生の備忘録 どもお久しぶりです!最近転職活動に注力している転職生です。 今回は、僕が受けたコーディングテストで少し厄介だなと思ったコーディングテストの一部を 紹介していきたいと思います! では早速いきましょう!! 指定の値になるまで逆数を足していくプログラム はてはて、どういったものか。ズバリ、1/1 + 1/2 + 1/3 ・・・ と繰り返していき、足した数がある数になるまで繰り返すというプログラムテストです! んで、そのある数になったタイミングで、逆数、つまり分数の母数(下の数)がいくらになっているのか 答えを出すといったような問題です!個人的にめんどくさくて、Rubyでの答えがあんまり載ってなかったので 参考になればなと思います! プログラムコード(答え) total = 0 num = 0 while total < 15 num += 1 total += (1.0 / (num)) end puts total puts num 以上が今回の逆数和を求めるコードになります! ※おまけ Javaのコードでのプログラム class sample1 { public static void main(String[] args) { double ans = 0.0; int count = 0; for(double i = 1.0; ans <= 15; i++) { count += 1; ans += 1 / i; } System.out.println(ans); System.out.println(count) ; } } それではまたお会いしましょう!!
- 投稿日:2021-08-09T17:40:13+09:00
Spring boot × postgres をHerokuへデプロイしてみる。(gradle編)
はじめに Herokuを使い始めて、色々とデプロイを試しているかなの忘却録です。 spring-boot(gradle) × postgresのでもアプリを作成し、Herokuへのデプロイ方法を記述しています。 基本的には色々な方が挙げているものと似通っていますが postgresの設定ファイルをgitignoreするとherokuへpushされず少し困ったのでその解決策なども書いています。 ※デモアプリのベースやHerokuの初期設定は、前の記事で上げたものを流用しています。 graldeのSpring BootアプリケーションをHerokuへdeployしてみる 環境 windows 10 Home java 11 spring-boot 2.3.5 gradle 7.1.1 (gradlew) heroku/7.56.1 win32-x64 node-v12.21.0 spring jpa 2.5.3 ソース https://github.com/Takeuchi713/spring-boot-gradle-heroku 目次 herokuの設定 デモアプリ作成 deploy gitignore対策 1. herokuの設定 Herokuのマイページでpostgresの設定をしていきます。 postgresを追加 マイページへログインし、アプリのダッシュボードへ移動する。 ResoucesからHeroku Postgresをアプリへ追加していく。 Add-onsにpostgresと入力すると選択肢が表示されるのでクリック。 追加するプランを聞かれるので、今回は無料のHobby Dev-Freeを選択。 Submit Order Formをクリックすると、Add-onsに追加され使用可能に。 Add-onsのHeroku Postgresをクリックすると自分専用のページが開く。 Setting => View Credenticalsをクリックすると、URLやpasswordなどpostgresへ接続するための情報が確認出来る。 2. デモアプリ作成 前回のアプリに単純なpostgresとの連携処理を追記していきます。 build-gradle postgresとspring-jpaの依存性を追加。 build.gradle dependencies { //追加 implementation 'org.springframework.boot:spring-boot-starter-data-jpa' runtimeOnly 'org.postgresql:postgresql' } Entity MyUser.java @Entity @Data @AllArgsConstructor @NoArgsConstructor @Table(name="MyUser") public class MyUser { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private String id; @Column(name = "name", nullable = false, length = 100) private String name; @Column(name = "email", unique = true, nullable = false, length = 200) private String email; @Column(name = "age", nullable = true, length = 3) private Integer age; @Column(name="gender", nullable =true , length = 1) private Integer gender; } Controller UserController.java @RestController @RequestMapping("/api") public class UserController { private final MyUserRepository repository; UserController(MyUserRepository repository) { this.repository = repository; } @GetMapping("/users") public ResponseEntity<List<MyUser>> getUsers() { //本来はService等に切り出すべき String email = "test@test.com"; MyUser user = repository.findByEmail(email).orElse(new MyUser(null, "takeuchi", "test@test.com", 18, 1)); repository.save(user); List<MyUser> users = repository.findAll(); return new ResponseEntity<List<MyUser>>(users, new HttpHeaders(), HttpStatus.OK); } } Heroku Postgresに接続するための設定 Heroku PostgresのSettingに記載されている内容を設定。 ※{Host},{Port}などはHerokuのSettingに書かれているものに置き換えて考えてください。 application.yml spring: datasource: url: jdbc:postgresql://{Host}:{Port}/{Database}?sslmode=require username: {User} password: {Password} driverClassName: org.postgresql.Driver jpa: database: POSTGRESQL show-sql: true #sqlをconsoleへ表示するか否か hibernate: ddl-auto: create #起動時にEntityからテーブルを自動生成し、終了時に削除する。ちなみに、updateとすると作成のみに変更可 動作確認 curl http://localhost:8080/api/users [{"id":"1","name":"takeuchi","email":"test@test.com","age":18,"gender":1}] 3. deploy 特に特殊なことなく、commitしpushすると自動でdeployされる git add . git commit -m "add postgres" git push heroku main ブラウザから動作確認 4. gitignore対策 以上で問題なくデプロイできましたが、、、 githubにソース登録しようとして、ymlをgitignoreすると Herokuへpush時もymlが無視されてしまい、デプロイに失敗し動かず... 隠したい部分をHerokuで環境変数として設定する方法で解決しました。 Herokuで環境変数を設定 アプリのページからSetting => Reveal Config Varsを選択し環境変数を表示する 環境変数を追加 POSTGRES_PASS, POSTGRES_USER, POSTGRES_URLを追加 ※DATABASE_URLはデフォであるので特に変更しないで大丈夫です。 spring-bootの設定を変更 ${}で囲った部分が環境変数で置き換えられます。 application.yml spring: datasource: url: ${POSTGRES_URL} username: ${POSTGRES_USER} password: ${POSTGRES_PASS} driverClassName: org.postgresql.Driver jpa: database: POSTGRESQL show-sql: true #sqlをconsoleへ表示するか否か hibernate: ddl-auto: create #起動時にEntityからテーブルを自動生成し、終了時に削除する。ちなみに、updateとすると作成のみに変更可 動作確認 gitignoreからymlを外して、上と同じようにdeployする。 git add . git commit -m "add postgres" git push heroku main 問題なく、動いていることを確認 curl "https://spring-gradle-deploy.herokuapp.com/api/users" [{"id":"1","name":"takeuchi","email":"test@test.com","age":18,"gender":1}]
- 投稿日:2021-08-09T15:32:06+09:00
【Java Silver】 関数型インターフェイス、ラムダ式対策
目的 現在、Java Silverの取得に向けて勉強中です。関数型インターフェイス、ラムダ式関係の問題の理解に苦しんだので重要ポイントをまとめました。本記事が同じような目標を持った方に少しでも役にたったら幸いです。 ラムダ式とは 関数型インターフェース:実装が必要なメソッドを一つだけもつインターフェースのこと。 public interface calcSum{ public abstract int sum(int num1, int num2); } ラムダ式:関数型インターフェースを実装する際に使われる式のこと。 通常、上記のCalcSumインターフェイスを実装し、利用する場合、以下のようにインターフェイスを実装し、実装したクラスをインスタンス化して利用します。 //実行用クラス public static void main(String []args){ calcSumImpl sum = new calcSumImpl(); int answer = sum.calcSum(5,4); System.out.println(answer); } //インターフェイスを実装したクラス class calcSumImpl implements CalcSum{ @Override public int calcSum(int num1,int num2){ return num1 * num2; } } 上記をラムダ式を使用して実装すると以下のようになります。メソッドは一つしかないので、メソッド名を省略できます。また、インスタンス化する処理も省略され、記述しているのは引数と{}内の実装部分だけです。 public static void main(String []args){ CalcSum calcSum = (int num1, int num2) -> { return num1 + num2; }; int answer = calcSum.calcSum(5,4); System.out.println(answer); } ラムダ式の基本構文は以下の通りです。 //構文 (引数) -> {}; 注意点①(記述の省略) また、以下のように引数の型や{}、returnの記述を省略することができます。 //引数の型を省略 CalcSum calcSum = (num1,num2) -> { return num1 + num2; }; //{}やreturnを省略(処理が一行のみの場合) CalcSum calcSum = (num1,num2) -> return num1 + num2; ただし、{}とreturnの省略、記述には以下の条件があります。 ・ラムダ式で{}を省略した場合には、returnを記述できない。また、処理は1文のみ。 ・ラムダ式で{}を記述した場合には、reutrnを省略できない。また、処理は複数行でも可。 注意点②(変数のスコープ) ラムダ式は、それを囲むボブロックと同じスコープをもつ。よって、ラムダ式を宣言しているブロックで宣言したローカル変数と同じ名前の変数はラムダ式内では宣言できません。また、ラムダ式外で宣言されたローカル変数にラムダ式内からアクセスするには実質的にfinalな変数でなければなりません。 そのため、以下のようにラムダ式内外で変数の値を変更しようとするとコンパイルエラーとなります。 interface Function { void test(); } public class Test { public static void main(String[] args) { String val = "hello"; Function f = () -> { //val = "hello!hello!" //コンパイルエラー System.out.println(val); }; //val = "GoodMorning!" //コンパイルエラー f.test(); //hello } } 関数型インターフェース 関数型インターフェイスはプログラマが自由に定義できるが、頻繁に使われるであろうものについては、java.util.finctionパッケージとして追加されました。Java Silverでは以下に示す4つが試験でよく出題されます。 関数型インターフェイス メソッド 説明 Consumer void accept(T) 引数を受け取って処理をする。結果を戻さない、引数の消費者。 Supplier T get() 何も受け取らずに結果だけを戻す供給者 Predicate boolean test(T) 引数を受け取ってそれを評価する「断定」 Function R apply(T) 引数を受け取って、指定された型(R)の結果を戻す「処理」
- 投稿日:2021-08-09T13:02:36+09:00
【Java】二次元Map: キーと値のペアを全て取得する
Mapとは Mapとは、キーと値のペアをリスト形式で保持するコレクションのことである。 取り出したい値のキーを指定することでその値を取得することができるが、Mapに格納されている全てのキーと値を取り出すにはどうしたらいいのだろうか 一次元Map: キーと値のペアを全て取得する MapSample1.java package sample; import java.util.HashMap; import java.util.Map; public class MapSample1 { public static void main(String[] args) { Map<Integer, String> map = new HashMap<>(); map.put(1, "aaa"); map.put(6, "bbb"); map.put(3, "ccc"); for (Map.Entry<Integer, String> entry : map.entrySet()) { System.out.println(entry.getKey() + ":" + entry.getValue()); } } } 結果 1:aaa 3:ccc 6:bbb 二次元Map: キーと値のペアを全て取得する やり方は一次元Mapのときと同様 MapSample2.java package sample; import java.util.HashMap; import java.util.Map; public class MapSample2 { public static void main(String[] args) { Map<Integer, Map<Integer, String>> map_list = new HashMap<Integer, Map<Integer, String>>(); Map<Integer, String> map1 = new HashMap<>(); map1.put(11, "aaa"); map1.put(12, "bbb"); map1.put(16, "ccc"); Map<Integer, String> map2 = new HashMap<>(); map2.put(21, "ddd"); map2.put(24, "eee"); map2.put(26, "fff"); Map<Integer, String> map3 = new HashMap<>(); map3.put(31, "ggg"); map3.put(32, "hhh"); map3.put(36, "iii"); map_list.put(1, map1); map_list.put(2, map2); map_list.put(3, map3); for (Map.Entry<Integer, Map<Integer, String>> map : map_list.entrySet()) { for (Map.Entry<Integer, String> entry : map.getValue().entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); } System.out.println(); } } } 結果 16: ccc 11: aaa 12: bbb 21: ddd 24: eee 26: fff 32: hhh 36: iii 31: ggg 以上
- 投稿日:2021-08-09T12:56:24+09:00
なぜ拡張for文は普通のfor文より速いのか?
はじめに 昔書いた記事を眺めていたら、「JavaでよくつかうCollection実装クラスの仕組みと特性」の記事が、「collection型のクラスであるlinkedListに関するランダムアクセスの遅さという課題への取り組み」という記事で参考にされていました。ありがとうございます! こちらの記事では、LinkedListで作成した処理について、for文と拡張for文で実行時間を比較しており、拡張for文のほうが圧倒的に早いという結果が出ています。 今回は、この拡張for文についてなぜ普通のfor文よりも高速か、というところを解説していきたいと思います。 拡張for文とは何ぞや? そもそも拡張for文とは何か? コレクション(ListやSetなど)の要素を繰り返し取り出して、何かしらの処理を行うときなどに使用する構文です。 例えば、Listに格納された数値の総和を取るために、for文と拡張for文ではそれぞれ下記のように記述します。 for文 List<Integer> list = Arrays.asList(1,3,5,7); for(int i = 0;i < list.size(); i++){ sum += list.get(i); } 拡張for文 List<Integer> list = Arrays.asList(1, 3, 5, 7); for(Integer i: list){ sum += i; } つまり、拡張for文は for(要素の型 要素を入れる変数: コレクションの変数){ 処理(要素が入った変数を処理に使用できる) } という構文になっていることがわかります。 普通のfor文と比べてシンプルな記述になっていますね。 拡張for文は何をやっているのか? 拡張for文が内部的にどういう動きをしているか、Javaのドキュメントを読んでみましょう。 ちなみに拡張for文と呼ばれることが多いですが、実はJavaのドキュメントではFor-Eachループという項目になっています。 これによると、下記2つのコードは等価であるとのこと。 その上で、拡張for文のほうがiterator変数を扱わないことから複雑性やエラーの危険性が排除されるのでより推奨されます。 拡張for文 List<Integer> list = Arrays.asList(1, 3, 5, 7); for(Integer i: list){ sum += i; } iterator List<Integer> list = Arrays.asList(1, 3, 5, 7); for(Iterator<Integer> i = list.iterator(); i.hasNext();){ sum += i; } つまり、拡張for文は内部的にはiteratorによる反復処理を行っているということがわかります。 なぜLinkedListだと拡張for文が格段に高速なのか? LinkedListの実装を考えてみましょう。 詳細な説明は「JavaでよくつかうCollection実装クラスの仕組みと特性」に記載がありますが、LinkedListは下図の通り、ノードが連結した形の構造になっています。 そのため、ノード3を参照するときには、ルートから始まり、ノード1、ノード2、ノード3と順番にノードをたどることになります。 ここで、for文を使った繰り返しで全要素にを参照する場合を考えると、それぞれのノードを参照するときの処理は下記のようになります。 ノード1:ルート⇒ノード1 ノード2:ルート⇒ノード1⇒ノード2 ノード3:ルート⇒ノード1⇒ノード2⇒ノード3 この場合、ノード3を参照するときに再度ルートから順番に辿ることになってしまい、効率が悪くなります。 そこで、LinkedListのIteratorでは、現在位置を保存することで、これを解消して効率よく内部の要素を返しています。 上述したように、拡張for文ではIteratorを使っているので、結果として拡張for文を使うことで効率の良い反復処理が可能になった、ということになります。 拡張for文とfor文のどちらを使うべきか? Iteratorは効率的な反復処理を提供するという前提に基づくならば、基本的に拡張for文を使うことで常に効率のいい反復処理が実行できます。 単純な配列の反復処理では、for文のほうが高速な場合もありますが、ほんの数ms程度なので変更に強いコードにするためにも拡張for文を使いましょう。 参考 for文と拡張for文の速度を比較してみる。 拡張for文を使う理由
- 投稿日:2021-08-09T08:30:05+09:00
【Java】BufferedImageの基本的な使い方
はじめに Javaにおいて標準機能で画像処理をしようとする場合、BufferedImageを用いることになると思います。 Javaはそもそも画像処理向きの言語ではないですし、OpenCVなどのライブラリを用いた方が簡単に処理できます。 しかし、ここではあえてBufferedImageの基本的な使い方を記しておこうと思います。 画像の読み込み ImageIOクラスを用いることで簡単に実装できます。 ImageUtil.java public static BufferedImage read(String filename) { try { return ImageIO.read(new File(filename)); } catch (IOException e) { e.printStackTrace(); } return new BufferedImage(0, 0, 0); } 画像の書き出し 読み込みと同じように実装できます。 ただし、拡張子情報が必要なのでsubstringで抽出します。 ImageUtil.java public static void write(BufferedImage bi, String filename) { try { String extension = filename.substring(filename.lastIndexOf(".") + 1); System.out.println(extension); ImageIO.write(bi, extension, new File(filename)); } catch (IOException e) { e.printStackTrace(); } } 画素値の取得 インデックス(x,y)の画素値を取得します。 ビットシフトをしてからビットマスクをすることで、8bit(0~255)の値を取得することができます。 先頭からAlpha, Red, Green, Blueの順に画素は並んでいるので、このようなプログラムになります。 ImageUtil.java public static int[] getRGB(BufferedImage bi, int x, int y) { int pixel = bi.getRGB(x, y); int red = pixel >> 16 & 0xff; int green = pixel >> 8 & 0xff; int blue = pixel & 0xff; return new int[]{red, green, blue}; } public static int[] getARGB(BufferedImage bi, int x, int y) { int pixel = bi.getRGB(x, y); int alpha = pixel >>> 24; int red = pixel >> 16 & 0xff; int green = pixel >> 8 & 0xff; int blue = pixel & 0xff; return new int[]{alpha, red, green, blue}; } 画素値の設定 インデックス(x,y)に画素値を設定します。 取得時と同様にビット演算することで値を設定できます。 ImageUtil.java public static void setRGB(BufferedImage bi, int x, int y, int[] rgb) { int pixel = 0xff000000 | rgb[0] << 16 | rgb[1] << 8 | rgb[2]; bi.setRGB(x, y, pixel); } public static void setARGB(BufferedImage bi, int x, int y, int[] argb) { int pixel = argb[0] << 24 | argb[1] << 16 | argb[2] << 8 | argb[3]; bi.setRGB(x, y, pixel); } 画像の表示 これはおまけですが、SwingのJFrameを使用することで、簡単に画像を表示することができます。 下のプログラムはあくまでもサンプルなので、コンストラクタを編集することでウィンドウの設定を変更できます。 ImageUtil.java /** * 画像を表示する * * @param bi BufferedImage型の画像 */ public static void show(BufferedImage bi) { new Viewer(bi); } /** * 画像表示用のフレーム */ private static class Viewer extends JFrame { /** * 表示する画像 */ BufferedImage bi; /** * コンストラクタ * * @param bi BufferedImage型の画像 */ public Viewer(BufferedImage bi) { this.bi = bi; setDefaultCloseOperation(EXIT_ON_CLOSE); setSize(bi.getWidth(), bi.getHeight()); setVisible(true); setResizable(false); } @Override public void paint(Graphics g) { g.drawImage(bi, 0, 0, null); } }
- 投稿日:2021-08-09T08:30:05+09:00
【Java】BufferedImageの簡単な使い方
はじめに Javaにおいて標準機能で画像処理をしようとする場合、BufferedImageを用いることになると思います。 Javaはそもそも画像処理向きの言語ではないですし、OpenCVなどのライブラリを用いた方が簡単に処理できます。 しかし、ここではあえてBufferedImageの基本的な使い方を記しておこうと思います。 画像の生成 コンストラクタを呼び出すだけです。 実際に動かす際にはwidth(幅)やheight(高さ), type(タイプ)に任意の値を入れてください。 ただし、typeは若干曲者なので、公式ドキュメントに目を通すことをお勧めします。 Sample.java var bi = new BufferedImage(width, height, type); 画像の読み込み ImageIOクラスを用いることで簡単に実装できます。 ImageUtil.java public static BufferedImage read(String filename) { try { return ImageIO.read(new File(filename)); } catch (IOException e) { e.printStackTrace(); } return new BufferedImage(0, 0, 0); } 画像の書き出し 読み込みと同じように実装できます。 ただし、拡張子情報が必要なのでsubstringで抽出します。 ImageUtil.java public static void write(BufferedImage bi, String filename) { try { String extension = filename.substring(filename.lastIndexOf(".") + 1); System.out.println(extension); ImageIO.write(bi, extension, new File(filename)); } catch (IOException e) { e.printStackTrace(); } } パラメータの取得 画像の幅、高さ、タイプは以下の通り取得できます。 サンプルのbiはBufferedImageのインスタンスです。 Sample.java int width = bi.getWidth(); int height = bi.getHeight(); int type = bi.getType(); 画素値の取得 インデックス(x,y)の画素値を取得します。 ビットシフトをしてからビットマスクをすることで、8bit(0~255)の値を取得することができます。 先頭からAlpha, Red, Green, Blueの順に画素は並んでいるので、このようなプログラムになります。 ImageUtil.java public static int[] getRGB(BufferedImage bi, int x, int y) { int pixel = bi.getRGB(x, y); int red = pixel >> 16 & 0xff; int green = pixel >> 8 & 0xff; int blue = pixel & 0xff; return new int[]{red, green, blue}; } public static int[] getARGB(BufferedImage bi, int x, int y) { int pixel = bi.getRGB(x, y); int alpha = pixel >>> 24; int red = pixel >> 16 & 0xff; int green = pixel >> 8 & 0xff; int blue = pixel & 0xff; return new int[]{alpha, red, green, blue}; } 画素値の設定 インデックス(x,y)に画素値を設定します。 取得時と同様にビット演算することで値を設定できます。 ImageUtil.java public static void setRGB(BufferedImage bi, int x, int y, int[] rgb) { int pixel = 0xff000000 | rgb[0] << 16 | rgb[1] << 8 | rgb[2]; bi.setRGB(x, y, pixel); } public static void setARGB(BufferedImage bi, int x, int y, int[] argb) { int pixel = argb[0] << 24 | argb[1] << 16 | argb[2] << 8 | argb[3]; bi.setRGB(x, y, pixel); } 画像の表示 これはおまけですが、SwingのJFrameを使用することで、簡単に画像を表示することができます。 下のプログラムはあくまでもサンプルなので、コンストラクタを編集することでウィンドウの設定を変更できます。 ImageUtil.java /** * 画像を表示する * * @param bi BufferedImage型の画像 */ public static void show(BufferedImage bi) { new Viewer(bi); } /** * 画像表示用のフレーム */ private static class Viewer extends JFrame { /** * 表示する画像 */ BufferedImage bi; /** * コンストラクタ * * @param bi BufferedImage型の画像 */ public Viewer(BufferedImage bi) { this.bi = bi; setDefaultCloseOperation(EXIT_ON_CLOSE); setSize(bi.getWidth(), bi.getHeight()); setVisible(true); setResizable(false); } @Override public void paint(Graphics g) { g.drawImage(bi, 0, 0, null); } }
- 投稿日:2021-08-09T01:16:09+09:00
CodeSignal - sudoku2にチャレンジ
コーディングしなさすぎてコーディングを忘れたエンジニアがコーディングを思い出すためにコーティングします。 今回は、アメリカのコーデイング練習サイトCodeSignalの問題「sudoku2」を解いていきます。 サイトによると、この問題はApple、UBERの面接で出されたことがあるようです。 問題 9×9マスの数独を表す、以下のような2次元配列が与えられます。 grid = [['.', '.', '.', '1', '4', '.', '.', '2', '.'], ['.', '.', '6', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '1', '.', '.', '.', '.', '.', '.'], ['.', '6', '7', '.', '.', '.', '.', '.', '9'], ['.', '.', '.', '.', '.', '.', '8', '1', '.'], ['.', '3', '.', '.', '.', '.', '.', '.', '6'], ['.', '.', '.', '.', '.', '7', '.', '.', '.'], ['.', '.', '.', '5', '.', '.', '.', '7', '.']] 数独をご存知の方ならわかると思いますが、数独のルールでは、縦9マス、横9マス、3×3の範囲それぞれで1〜9の文字が重複してはいけません。 3×3の範囲というのは、次の枠組み内のことを意味しています。 この問題では、この解き途中みたいな数独について上記の3点のポイントをチェックし、条件を満たしていればture、そうでなければfalseを返す関数を書きます。 たとえば、上の例なら縦9マス、横9マス、3×3の範囲どれをチェックしても数値は重複してないので、答えはtrueです。 一方、下の例なら答えはfalseです。 真ん中の下段の3×3の範囲に2が2回登場しているからです。 grid = [['.', '.', '.', '.', '2', '.', '.', '9', '.'], ['.', '.', '.', '.', '6', '.', '.', '.', '.'], ['7', '1', '.', '.', '7', '5', '.', '.', '.'], ['.', '7', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '8', '3', '.', '.', '.'], ['.', '.', '8', '.', '.', '7', '.', '6', '.'], ['.', '.', '.', '.', '.', '2', '.', '.', '.'], ['.', '1', '.', '2', '.', '.', '.', '.', '.'], ['.', '2', '.', '.', '3', '.', '.', '.', '.']] 解き方・考え方 まず普通に考えたら、縦、横、3×3のそれぞれについて重複を確認し、どれか一つでも重複があればその時点でfalseを返せばいいですよね。 、、、 それしか思い浮かびませんでした。 Javaで書くとこんな感じです。 boolean sudoku2(char[][] grid) { boolean answer = true; HashSet<Character> seen = new HashSet<>(); char target; // check row for (int i=0; i<grid.length; i++) { seen.clear(); for (int j=0; j<grid[i].length; j++) { target = grid[i][j]; if (seen.contains(target) && target != '.') { return false; } else { seen.add(target); } } } // check column for (int i=0; i<grid.length; i++) { seen.clear(); for (int j=0; j<grid[i].length; j++) { target = grid[j][i]; if (seen.contains(target) && target != '.') { System.out.println("check column false"); return false; } else { seen.add(target); } } } // check subgrid for(int startRow=0; startRow<9; startRow+=3){ for(int startColumn=0; startColumn<9; startColumn+=3){ //loop inside grid seen.clear(); for(int i=startRow; i<startRow+3; i++){ for(int j=startColumn; j<startColumn+3; j++){ target = grid[i][j]; if(seen.contains(target) && target != '.') { return false; } else { seen.add(target); } } } } } return true; } コードの説明:seenというHashSetを用意し、その中にチェックした数値を入れて重複していないか確認しています。チェックする観点が3つあるので、それぞれ上から行(// check rowの箇所)、列(//check columnの箇所)、3×3(// check subgridの箇所)と固まりがあります。 さて、これでもテストは通ったのですが、他の方のコードを見るともっとはるかにいい方法がありました。 そのまま転載は微妙なので、アイデアはパクってコードは少し変えて掲載します。 boolean sudoku2(char[][] grid) { Set<String> seen = new HashSet<String>(); for (int i=0; i<grid.length; i++){ for (int j=0; j<grid.length; j++){ if (grid[i][j] != '.' && !seen.add(grid[i][j] + " in row " + i)) {return false;} if (grid[i][j] != '.' && !seen.add(grid[i][j] + " in col " + j)) {return false;} if (grid[i][j] != '.' && !seen.add(grid[i][j] + " in row " + i/3 + " " + j/3)) {return false;} } return true; } } コードの説明:seenにチェックした値を入れるのは前のコードと同じですが、この場合はループは2個しか使っていません。2次元配列の値を先頭から一つずつチェックしていき、ただチェックした値を入れるのではなく、こんな感じで文字列として保存します。 1 in row 0 1 in column 0 1 in subgrid 0 このおかげで、たとえば同じ1でも「X行目に出てきた1」「X列目に出てきた1」「ここの3×3で出てきた1」のように別ものとして保存できます。 そのため、ループは一回でも一気に3つの観点で重複を確認できます。 最後に、HashSetのaddは追加しようとする値と同じ値がすでにある場合はfalseを返すので、falseの場合(重複している場合)はreturn falseとします。 この考え方にたどり着くには、まずはループを少なくしようとすることだと思います。 次に、確認観点が3つあるからといって、同じ値を別のところでそれぞれ確認するのは無駄、という考え方をしなきゃいけない気がします。 ここまでくれば上の例のようにスマートには解けなくても、「じゃあHashSetを3つ作って、配列の値を一つづつ見ていきながらそれぞれに格納していこう」、などという発想が生まれそうです。