- 投稿日:2020-09-21T23:34:20+09:00
【Java】Enumでif文を少なくする
はじめに
保守・開発でプログラムに触れる際、その8割はソースコードを読む時間に費やされるというのはよく聞く話です。
そのため、可読性の高く、保守性の高いソースコードというのは常に意識されるべきですが、どうしても業務システムとなると、ある契約の種類だとか、取引先の種類だとか、種類による条件分岐がどうしても多くなりがちだと感じています。
そこで、if文を少なくなるする方法の1つとしての、Enumを使用した方法があるということを知ったので、忘れないように投稿します。if文を少なくするメリット
if文による条件分岐が多い場合、Enumを使用することでとても読みやすくなります。
具体例
SampleEnum.javapublic enum SampleEnum { kind1(new Kind1()), kind2(new Kind2()), kind3(new Kind3()), kind4(new Kind4()); SuperKind kind; private SampleEnum(SuperKind kind) { this.kind = kind; } public void execute(Object obj) { kind.execute(obj); } }まずはEnumを用意します。
次に、各プロパティに対して、実行したい処理クラスを記載し、コンストラクタを定義します。
最後に、実行したい処理クラスの実行メソッドを定義します。Enum自体は、この3ステップで実装完了です。
あとは、種類が追加された場合は、プロパティを追加して、その種類ごとに実行したい処理クラスを記載するだけです。
Kind1.javapublic class Kind1 implements SuperKind { public void execute(Object obj){ if(!(obj instanceof String)) { System.out.println("引数には文字列を指定してください"); } System.out.println("Kind1のクラスで" + obj); } }実行したい処理クラスです。
ここでは簡単に、そのクラスで実行されたことがわかる文を、コンソールに出力するようにします。
SuperKindは、interfaceです。ここでは中身はexecute
メソッドを定義しているだけなので、割愛します。
Main.javapublic class Main { public static void main(String[] args) { SampleEnum test1 = SampleEnum.valueOf("kind1"); SampleEnum test2 = SampleEnum.valueOf("kind2"); SampleEnum test3 = SampleEnum.valueOf("kind3"); SampleEnum test4 = SampleEnum.valueOf("kind4"); String string = "Enumのテスト"; test1.execute(string); test2.execute(string); test3.execute(string); test4.execute(string); } }mainクラスです。ここで実行した結果は、以下の通りになります。
Kind1のクラスでEnumのテスト Kind2のクラスでEnumのテスト Kind3のクラスでEnumのテスト Kind4のクラスでEnumのテスト
最後に
このような実装をすることで、Factoryクラスでのif文判定を減らすこともできれば、どの業務ロジック処理を呼び出すかなどのif文判定を減らしたりなど、活用の幅は大きいかと感じました。
- 投稿日:2020-09-21T21:43:48+09:00
【Eclipse】補完機能を使いたいけど、スペースで補完を確定してしまうのをなんとかしたい
現象
使用しているエディタ
Eclipse 4.8.0
macOS Catalina 10.15.6Eclipseの補完機能を有効にして、トリガーには
.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
を当てている。String str =と入力したいのに、strの後に押すspaceキーで補完が確定される。つまり、自分で決める変数名でも意図しない補完が適応されてしまうので、バックスペースで毎回消去しないといけなくなる。いやだ〜
解決
自動有効化遅延を500に設定
環境設定ページはショートカット「⌘,」で開く。
上の画像のように、設定を変更します。補完候補が表示されるまでに遅延があるので、すばやくSpaceキーを押せば大丈夫でした。
新しいバージョンのEclipseではEnterキーだけで確定をするというチェックボックスがあるみたいですが、自分の(Eclipse4.8.0)はなかったです。
- 投稿日:2020-09-21T20:00:16+09:00
2020年に最も人気があるプログラミング言語 10選
元記事: https://jp.scrapestorm.com/tutorial/10-most-popular-programming-languages-in-2020/
プログラミングの初心者について、より良い見通しがある言語を選択するは非常に重要です。本文は十種類の2020年に最も人気があるプログラミング言語を紹介します。
まずTIOBEランキングを見てみます。非常に権威のあるランキングです。次の14つは長く生きているので、SQLがGOに置き換えられたことを除いて、他の9つのプログラミング言語がすべて存在し、長い間リストを支配していることがわかります。これから一つずつ紹介しましょう。
1.Java
Javaは実際にはC ++の代替品です。Sunは当初、C ++よりも簡単なオブジェクト指向プログラミング言語を開発したいと考えていました。時間の経過とともに、Javaは学習とクロスプラットフォーム化が容易になるため、Javaの人気はC ++の人気をはるかに超えています。
Java仮想マシンの助けを借りて、JavaはLinux、Windows、Mac-OSなどのさまざまなオペレーティングシステムで自由に使用できるため、エンタープライズレベルの開発で非常に人気があります。2.C++
名前からわかるように、C ++はC言語の拡張機能であり、C言語のオブジェクト指向関数の作成を目的としています。
C ++はすべてのプラットフォームで実行でき、あらゆるタイプのハードウェアを効果的に利用できるため、リソースが限られたプラットフォームで最高のパフォーマンスを発揮できます。3.C
C言語は、1972年にAT&Tベル研究所のデニス・リッチーが主体となって開発した汎用プログラミング言語である。それは、システムリソースを効果的に使用できるを目指します。当時、メモリのすべてのバイトが高価だったからです。C言語は非常に早い時期に誕生しましたが、依然として最も一般的に使用されているプログラミング言語の1つです。
C ++と同様に、Cもメモリに直接アクセスしてハードウェアを制御できます。 Cはオペレーティングシステムと密接に関連しており、プログラマーはメモリ割り当ての詳細を処理する必要があるため、把握するのが困難です。4.Python
学習コストは非常に低いため、すべてのプログラマーはPythonを好みますが、現在の非常に深い人工知能、機械学習、データ分析などのアプリケーションレベルは非常に高くなっています。
Pythonの構文はシンプルでエレガントで、コミュニティも非常に活発です。 しかし、言うべきことの1つは、Pythonの職位には高い学歴が必要であることです。5.JavaScript
JavaScriptは高級なダイナミンクプログラミング言語です。非常に人気のあるフロントエンドフレームワークVue.jsはjsJavaScriptで作成しました。フロントエンド開発に従事したい場合は、JavaScriptは必須であると言えます。
6.C
当初、C#はJavaのコピーと見なされていましたが、それらには著しい類似点があり、Javaとほぼ同じ構文であり、コンパイルして実行する必要があります。 時間の開発とMicrosoftによる多大な努力により、C#は豊富なクラスライブラリとフレームワークを蓄積し、開発者はこれに基づいて.NETプラットフォームに基づくさまざまなアプリケーションをすばやく作成できます。
7.Swift
Swiftは、Appleによって作成された強力で直感的なプログラミング言語であり、iOS、Mac、Apple TV、およびApple Watch用のアプリの開発に使用できます。 開発者に完全な自由を提供することを目的としています。 Swiftは使いやすくオープンソースであり、アイデアさえあれば、誰でも素晴らしいものを作成できます。
8.Go
Go言語のデザインは非常に洗練されており、使用方法も非常に簡単で、開発と拡張を解決する能力も非常に優れています。 重要なのは、学習が非常に簡単であり、これらの利点がGo言語の急速な成長に貢献していることです。
Google、AWS、Cloudflare、CoreOSなどはすべて、クラウドコンピューティング関連製品の開発にGolangを大規模に使用し始めています。 未来はとても明るいと言えます。9.PHP
PHPがWebアプリケーションを開発したのは35年以上の歴史があります。PHPは常にWeb開発の王様でした。特に、WordPressなどのコンテンツ管理プラットフォームの人気とFacebook(PHPが開発した)の支持が相まって、業界でのPHPの地位が強化されました。
10.Ruby
Rubyはもともとオブジェクト向けのスクリプトプログラミング言語でしたが、時間が経つにつれて、徐々に解釈された高水準の汎用プログラミング言語に発展しました。 開発者の生産性を向上させるのに非常に役立ちます。シリコンバレーでは、Rubyは非常に人気があり、クラウドコンピューティング時代のWebプログラミング言語として知られています。
- 投稿日:2020-09-21T19:02:40+09:00
Java static 【個人まとめ】
【Java static】
Java static について理解が浅かったのでいろいろ調べてまとめました。
ご指摘など頂けると嬉しいです。随時追加して、備忘録として使っていきます。
staticメソッド
このメソッドは、あるクラスのどのインスタンスから呼ばれても処理内容が変わらない。
staticメソッドは、インスタンス化による影響を受けない。クラス名.メソッド名の形で、インスタンスなしで呼び出せる。
Javaのmainメソッドには、static(最初だから)が必ず必要である。非staticメソッド
staticを使わない変数は、クラスをインスタンスしないと使えない。
→非static変数変数
・クラス変数 クラス内 グローバル変数
→インスタンス全て(クラスから作られた)で共有される。・インスタンス変数 インスタンス固有 ローカル変数
→インスタンス1個の中だけで使われる。
- 投稿日:2020-09-21T18:40:53+09:00
Kotlin について調べたこと
Kotlin について調べたこと
kotlinとは
Kotlinは、IntelliJ IDEAで有名なJetBrainsが開発したオブジェクト指向プログラミング言語です。
コンパイルされたコードはJVM上で動作するため、
これまでJAVAで作成した資産を流用できる様になっています。Kotlinの基本構文
変数宣言
// val で宣言すると値の再代入ができなくなる val firstName: String = "Tanaka" // 型推論でいちいち型の定義がいらない val lastName = "Taro" // var で宣言すると再代入ができる var age = 20 age = 21配列
// 配列(array)を作成 val nameList: Array<String> = arrayOf("tanaka", "saitou", "kimura") println(nameList[0]) // tanaka が出力 // 配列の要素の書き換え nameList[1] = "sakurai" println(nameList[1]) // sakurai が出力 // 配列(リスト)を作成 val animalList: List<String> = listOf("dog", "cat", "rabbit") println(animalList[0]) // dog が出力 // 配列の要素の書き換え animalList[1] = "tiger" // エラー インターフェイスListは読み取り専用List でも mutableListOf() で変更可能なリストができるので Array と List の使い分けがいまいちわからないです。
マップ
// マップ作成 val numberMap: MutableMap<String, Int> = mutableMapOf("one" to 1) println(numberMap["one"]) // 1 が出力 //// 値の追加 numberMap["tow"] = 2 println(numberMap) // {one=1, tow=2} が出力 // mapOf でマップ作成 val reNumberMap: Map<Int, String> = mapOf(1 to "one") println(reNumberMap[1]) // 1 が出力 //// 値の追加 reNumberMap["tow"] = 2 // エラー mapOf は 読み取り専用条件分岐
// 普通のif文 if (true) { println("if") // if が出力 } else { println("else") } // Kotlinに三項演算子はないのでそれっぽいの val animal = "dog" val isDog = if (animal == "dog") true else false println(isDog) // true が出力 // 空チェックとかなら val person:String? = null val personName = person?: "NoName" println(personName) // NoName が出力 // when文 switch文と同じような感じ val result = when("hoge") { "hoge" -> "hoge" "fuga" -> "fuga" else -> "else" } println(result) // hoge が出力ループ処理
// while 文 var count = 5 while(0 < count--) { println("while count: ${count}") } // for 文 for (i in 1..5) { println("for count: ${i}") }JAVA と Kotlin の書き比べ
userクラスに年齢、名前、性別を加え、
自己紹介メソッドを実行するコードを書いてみました。Kotlin
user.kt
fun main() { val user = userData(20, "Taro", "Men") user.selfIntroduction() }userData.kt
data class userData ( var age: Int? = 0, var name: String = "NoName", var gender: String? = null ) { fun selfIntroduction() { println("My name is ${name}") println("Age is ${age}") println("Gender is ${gender}") } }JAVA
user.java
public class user { public static void main(String[] args) { userData user = new userData(20, "Taro", "Men"); user.selfIntroduction(); } }userData.java
public class userData { private int age; private String name; private String gender; public userData(int age, String name, String gender) { this.age = age; this.name = name; this.gender = gender; } public void selfIntroduction() { System.out.println("My name is " + this.name); System.out.println("Age is " + this.age); System.out.println("Gender is " + this.gender); } }記述して感じた違いとしては
- コンストラクタの作成に New がいらない
- 型推論の機能により必ず型の定義をする必要がない
- 末尾のセミコロンが必須ではない
- 文字列リテラルの中に式を入れることができる
Spring Boot で Hello World
環境
Windows10 Pro
IntelliJ Community Edition 2020.2
JAVA 14プロジェクトの雛形を作る
Spring Initializr を用いてプロジェクトの雛形を入手する。
設定は
Project : Gradle Project
Language : Kotlin
Spring Boot : 2.3.4
Project Metadata : デフォルトのまま
Packaging Jar
Java : 14
Dependencies : Spring Web最後に、GENERATE を押下してプロジェクトの雛形をダウンロード
作成したプロジェクトの雛形をビルド
IntelliJ から File→Open...→ダウンロードしたプロジェクトの雛形を選択→OK の手順で展開
コントローラを作成
demo/src/main/kotlin/com.example.demo/controller
フォルダーに対して
右クリック→New→Kotlin File/Class を押下
HelloController.kt を作成する。HelloController.kt
package com.example.demo.controller import org.springframework.web.bind.annotation.RestController import org.springframework.web.bind.annotation.RequestMapping @RestController public class HelloController { @RequestMapping("/") fun hello():String { return "Hello World" } }Webサーバの起動
右上の 再生ボタン を押下し、ビルドを行う
接続
ビルドが終わったら
http://localhost:8080
に接続感想
筆者はJAVAをあまり触ったことが無いので、
JAVAを知らないでいきなりKotlinって大丈夫なのかなと不安を感じていたのですが、
いきなりKotlinから書き始めてみたところ、いきなりKotlinでも問題なく
むしろKotlinから始めたほうがとっつきやすいと感じました。
JAVAと書き比べてみたところ、Kotlinでのコード量が少なくなり、
型推論やデータクラス等がコードを簡潔の手助けになっていると感じました。
- 投稿日:2020-09-21T18:01:05+09:00
Azure Batch で Java アプリケーションを実行する
はじめに
Azure Batch はオートスケール可能な仮想マシン (VM) 上で任意のスクリプトやアプリケーションを実行できる便利なサービスです。VM に JVM (Java Virtual Machine) がインストールされていれば Java アプリケーションも実行することができます。
公式ドキュメントでは .NET や Python アプリケーションについてのクイックスタートがありますが Java のドキュメントは現状無いため、本記事では、Azure Batch 上で Java アプリケーションを実行する流れを簡単にまとめたいと思います。
大筋は以下のクイックスタートの記事に沿っていますので、合わせてご覧ください。
Azure クイックスタート: Azure portal で最初の Batch ジョブを実行する - Azure Batch | Microsoft DocsBatch 関連リソース作成
それでは順に Batch 関連のリソース (アカウント、プール、ジョブ、タスク) を作成していきます。各リソースの大まかな解説は以下の通りです。
- アカウント : Azure Batch の最上位のリソースです。
- プール : VM の集合であり、計算資源としての役割を持ちます。1 つのアカウントに複数のプールを作成することができます。
- ジョブ : 計算の単位であるタスクの集合です。ジョブは 1 つまたは複数のプールに関連付けることができます。配下のタスク群は、ジョブに関連付けられたプールを計算資源として利用します。
- タスク : 計算の単位です。アプリケーションの実行コマンドなどを定義します。
Batch アカウント作成
Azure ポータルの検索ボックスで [Batch] と入力すると [Batch アカウント] が候補に表示されますのでクリックします。
新しい Batch アカウントの画面では任意のリソースグループ名やアカウント名などを入力します。Batch アカウントにストレージアカウントを関連付けることができますので [ストレージ アカウントの選択] のリンクをクリックします。
既存のストレージアカウント、または新規作成を選ぶことができます。ここでは新規でストレージアカウントを作成します。特にこだわりが無ければ推奨されている StorageV2 (汎用 v2) を選択します。レプリケーションは要件に合わせて任意のレベルを設定ください (ここでは最もコストが安いローカル上長ストレージを選択しています)。
プールの追加
Batch アカウントの左側メニュー [プール] にて [追加] をクリックします。
ここで VM (の集合) を作成するため、設定項目がかなり多いです。まずイメージですが、様々な種類を選ぶことができます。ここでは以下の内容を選択しています。
- 発行者 : canonical
- オファー : ubuntuserver
- SKU : 18.04-lts
選択可能なイメージについては本記事末尾に表形式でまとめています。
APPENDIX - 選択可能なイメージの種類選択可能な VM サイズは以下のドキュメントをご覧ください。
プールの VM サイズを選択する - Azure Batch | Microsoft Docsここでは VM サイズはデフォルトの [Standard A1] を選択しています。スケーリングは台数固定か自動スケールを選択できますが、ここでは [固定] とし、1 台の VM が起動するようにしています。
開始タスクにて VM が起動する際に実行するコマンドを定義できます。[有効] を選択します。
(開始タスク)
先に選択した Ubuntu 18.04-lts のイメージには JVM がインストールされていないため、開始タスクの [コマンドライン] に JVM のインストールコマンドapt install -y openjdk-11-jdk
を定義します。合わせて [ユーザーID] を [プール autouser、管理者] に変更します。残りの設定は任意で、問題なければ [OK] をクリックします。
プールの追加では他にも仮想ネットワークなど色々な設定ができますが、ここでは最小限とし [OK] をクリックします。
これでプールの作成は完了です。専用ノードが 0 -> 1 になっていますが VM の起動中を意味します。これが 1 になったら VM の起動は完了です。もし 1 にならない場合はノードの起動に失敗しています。開始タスクが正しく設定されていない可能性などがあるため、ミスなどが無いか見直してみましょう。
ジョブの追加
Batch アカウントの左側メニュー [ジョブ] にて [追加] をクリックします。
任意のジョブ ID、関連付けるプールを選択し [OK] をクリックします。
モード & 詳細設定
モードと詳細設定でより細かい制御を行うことができますが、ここでは特に設定せずに参考としてキャプチャだけ載せておきます。
タスクの追加 (Java 動作確認)
Java が問題なくインストールされていることを確認するため、Java のバージョン確認のコマンドをタスクにて実行していきます。
任意 & ジョブ内で一意なタスク ID を入力、コマンドラインに
java --version
を入力し、[送信] をクリックします。
すると、タスクはジョブのキューに送信され、アクティブ → 実行中 → 完了という状態遷移をします。他のタスクが実行されていない状態のためあまり待たずにタスクの実行が完了するはずです。詳細を確認するため送信したタスクをクリックします。
正常にタスクが完了していれば、標準エラーへの出力結果「stderr.txt」や標準出力への出力結果「stdout.txt」などが表示されるはずです。stdout.txt をクリックします。
以下のように Java のバージョン情報が出力されていれば OK です。
これで Java アプリケーションを実行するための Batch 関連リソースの準備が整いました。
Java アプリケーションの実行
ここから本題の Java アプリケーションの実行に入ります。実行可能 JAR ファイルをストレージアカウントにアップロードし、その JAR ファイルを Batch のタスクでダウンロードして実行する流れになります。
実行可能 JAR ファイルの準備
適当な実行可能 JAR ファイルを準備します。ここでは以下の Spring Boot + Sprint Batch のサンプルアプリケーション作成の手順を進めることで生成できる実行可能 JAR ファイルを利用します。
https://spring.io/guides/gs/batch-processing/
JAR ファイルをストレージアカウントにアップロード
Batch アカウントに関連付けたストレージアカウントに適当な Blob コンテナーを作成して JAR ファイルをアップロードしておきます。
タスクの追加 (Java アプリケーション実行)
Batch アカウントのジョブからタスクの追加を行います。コマンドラインでは以下の JAR 実行のコマンドを入力します。JAR ファイルのダウンロード設定のため [リソースファイル] をクリックします。
java -jar batch-processing-0.0.1-SNAPSHOT.jarリソースファイルの設定画面で [ストレージ BLOB の選択] をクリックします。
[SAS を含める] にチェック、[有効期限] は任意の値 (ここではデフォルトの 7) を入力し [OK] をクリックします。
アップロードした JAR ファイルを指定して [選択] をクリックします。
[送信] をクリックします。これにより、タスクで定義したコマンドラインを実行する前に、当該 JAR ファイルが VM の作業ディレクトリにダウンロードされます。
タスクの送信 & 実行完了後、以下のように状態が完了になっており、stdout.txt に標準出力結果が出力されていれば、処理は正常に完了しています。
本編は以上となります。
APPENDIX - 選択可能なイメージの種類
2020 年 9 月 20 日時点でイメージの種類 - Marketplace から選択可能な項目の一覧です。あくまで一時点のスナップショットですので、最新情報は Azure ポータルからご確認ください。
イメージの種類
- Marketplace
- Cloud Services (Windows のみ)
- カスタム イメージ - 共有イメージギャラリー
- グラフィックスとレンダリング
イメージの種類 - Marketplace
発行者 オファー SKU canonical ubuntuserver 16.04-lts canonical ubuntuserver 18.04-lts credativ debian 8 credativ debian 9 debian debian-10 10 micrsoft-azure-batch centos-container 7-7 micrsoft-azure-batch centos-container-rdma 7-4 micrsoft-azure-batch centos-container-rdma 7-7 micrsoft-azure-batch ubuntu-server-container 16-04-lts micrsoft-azure-batch ubuntu-server-container-rdma 16-04-lts micrsoftwindowsserver windowsserver 2008-r2-sp1 micrsoftwindowsserver windowsserver 2008-r2-sp1-smalldisk micrsoftwindowsserver windowsserver 2012-datacenter micrsoftwindowsserver windowsserver 2012-datacenter-smalldisk micrsoftwindowsserver windowsserver 2012-r2-datacenter micrsoftwindowsserver windowsserver 2012-r2-datacenter-smalldisk micrsoftwindowsserver windowsserver 2016-datacenter micrsoftwindowsserver windowsserver 2016-datacenter-smalldisk micrsoftwindowsserver windowsserver 2016-datacenter-with-containers micrsoftwindowsserver windowsserver 2019-datacenter micrsoftwindowsserver windowsserver 2019-datacenter-core micrsoftwindowsserver windowsserver 2019-datacenter-core-smalldisk micrsoftwindowsserver windowsserver 2019-datacenter-core-with-containers micrsoftwindowsserver windowsserver 2019-datacenter-core-with-containers-smalldisk micrsoftwindowsserver windowsserver 2019-datacenter-smalldisk micrsoftwindowsserver windowsserver 2019-datacenter-with-containers micrsoftwindowsserver windowsserver 2019-datacenter-with-containers-smalldisk 以上です。
- 投稿日:2020-09-21T17:57:25+09:00
[Thymeleaf テンプレートフラグメントで共通化] Headerの作成
こんにちは。
今回の記事ではSpring, Thymeleafで作るTODOアプリのヘッダー部分の作成について触れてみたいと思います。TODOアプリ作成リンク集
1: [超基礎の理解] MVCの簡単な説明
2: [雛形を用意する] Spring Initializrで雛形を作ってHello worldしたい
3: [MySQLとの接続・設定・データの表示] MySQLに仮のデータを保存 -> 全取得 -> topに表示する
4: [POST機能] 投稿機能の実装
5: [PATCH機能] TODOの表示を切り替える
6: [JpaRepositoryの簡単な使い方] 検索機能の実装
7: [Thymeleaf テンプレートフラグメントで共通化] Headerの作成(今ここ)現状のページ構成
・ http://localhost:8080/top
・ http://localhost:8080/search現状はこの様に表示するのは2ページだけです。
今後このTODOアプリではViewとして
・エラーを表示するページ
・編集ページを作るので、フロント表示領域としては合計4ページになる予定です。
そしてそれら4ページ全てに共通のヘッダーがつく、というのが仕様になります。
4ページぐらいであれば、個々のHTMLに
html<header>....</header>と追加していってもいいのですが
ヘッダーに何か新しいリンクを追加したい際に、一つのページを編集すれば全ページに反映されるようになるので編集がとても楽になります。
テンプレートフラグメントって?
テンプレートフラグメントとは共通化したいHTMLを管理するThymeleafの機能です。
1つのHTMLファイルや要素を複数のHTML内で表示する時に使用します。
この考え方はVue.jsとかReactで使われるコンポーネントという概念に近いと思うので意識してみると良いかもしれません。
さてテンプレートフラグメントの使い方ですが
これは
1. 複数のページで表示したい要素をフラグメント化して
2. 使いたいページでそのフラグメントを呼び出す
に尽きます。
HTMLファイルの位置関係
今回はヘッダーを
main/resources/templates/common/header.html
こんなパスで作成しフラグメント化します。
そしてそれを
main/resources/templates/top.html
main/resources/templates/search.htmlのヘッダー内に表示してみましょう!
HTML要素をフラグメント化する
さてそれではフラグメント化を行ってみましょう。
まずはフラグメント化という概念を簡単に説明します。
フラグメント化ってどういう意味?
フラグメントとは”かけら・断片・破片”という意味です。
つまりフラグメント化とはHTMLの要素を一つのかけらとして登録(変数化)するイメージ良いでしょう!
フラグメント化のやり方
共通化したい要素(例えばheader)に
<header th:fragment="header_fragment()> ... </header>
th:fragment="フラグメント名(引数を渡したいときは引数)
としてやる事でフラグメント化できます。
ヘッダーをフラグメント化してみる
main/resources/templates/common/header.html<!DOCTYPE html> <html lang="ja" xmlns:th="http://www.thymeleaf.org"> <header th:fragment="header_fragment()"> <div class="text-center bg-primary"> <div class="d-flex p-3"> <p class="display-4 w-75 pl-5 mb-0 text-left font-weight-bold"> <a th:href="@{/top}" class="text-dark">ToDoリスト</a> </p> <p class="display-4 w-25 mb-0 mr-5 text-right font-weight-bold "> <a th:href="@{/search}" class="text-dark">検索</a> </p> </div> </div> </header> </html>見ての通り、
<header th:fragment="header_fragment()">
と書いてあげる事でこのヘッダーはフラグメント化されています。これを別のHTML上から呼び出してみましょう!
フラグメント化してViewを別のHTMLから呼び出す。
main/resources/templates/top.html<body> <header th:replace="common/header :: header_fragment()"></header> .....略 </header> </body>注目したいのは
th:replace="...."
の部分です。replaceは置き換えるという意味を持っており、文字通りそのHTML要素を置き換えてしまいます。
なのでまるっと上書きするイメージです。他にも
th:insert
やth:include
という表示方法もあるので使い分けたい方は調べてみると良いでしょう。
th:replace="呼び出したいフラグメントの相対パス :: 呼びたいフラグメント名"
とする事でそのフラグメントを呼ぶことができます!
これで今後Viewが増えても簡単に共通のヘッダーを呼び出すことができる様になりました!
まとめ
テンプレートフラグメントの使い方をざっくり書くと
・共通化したいViewをフラグメント化する(th:fragment)
・そのViewを呼び出す(th:replace, th:insert, th:include)
となります!
今回はreplaceで要素をまるっと上書きしていますが、仕様によっては一部分だけ共通化したものを呼び出したい(例えばボタンとかですかね)とかあると思うので、その際はinsertかincludeを使ってみましょう!
- 投稿日:2020-09-21T17:38:22+09:00
Javaに初めて触れてみた③
Javaに触れてみた
自己満の備忘録になりますのでご容赦ください。
いろいろ作ってみる
今までの知識を活かして、私が大ファンの広島東洋カープについて機能を作っていきたいと思います。
①監督の在任期間を表示する
番号を選択し、配列から取り出す機能にします。
Test.javaimport java.util.Scanner; class Test { public static void main(String args[]) { System.out.println("[1]達川監督\n[2]山本監督\n[3]ブラウン監督\n[4]野村監督\n[5]緒方監督\n[6]佐々岡監督\n1〜6の数字を選択してください。"); Integer number = new Scanner(System.in).nextInt(); String[] array = {"2000年", "2001年〜2005年", "2006年〜2009年", "2010年〜2014年", "2015年〜2019年", "2020年"}; System.out.println(array[number - 1]); } }ターミナル[1]達川監督 [2]山本監督 [3]ブラウン監督 [4]野村監督 [5]緒方監督 [6]佐々岡監督 1〜6の数字を選択してください。 4 2010年〜2014年import java.util.Scanner;を書き忘れてしまい、Scannerが使えませんでしたので、注意します。
↓エラー文ターミナルTest.java:4: エラー: シンボルを見つけられません Integer number = new Scanner(System.in).nextInt(); ^ シンボル: クラス Scanner 場所: クラス Test②4択クイズ
4択から数字を選んで簡単な条件分岐させます。
Test2.javaimport java.util.Scanner; class Test2 { public static void main(String args[]) { System.out.println("2019年ドラフト1位の森下選手の出身大学は?\n[1]明治大学\n[2]法政大学\n[3]早稲田大学\n[4]立教大学"); Integer number = new Scanner(System.in).nextInt(); if (number == 1) { System.out.println("正解です!"); } else { System.out.println("間違いです"); } } }ターミナル2019年ドラフト1位の森下選手の出身大学は? [1]明治大学 [2]法政大学 [3]早稲田大学 [4]立教大学 2 間違いですターミナル2019年ドラフト1位の森下選手の出身大学は? [1]明治大学 [2]法政大学 [3]早稲田大学 [4]立教大学 1 正解です!③4択クイズの応用
間違えたらその選択肢を減らす。
Test3.javaimport java.util.Scanner; import java.util.ArrayList; import java.util.List; class Test3 { public static void main(String args[]) { List<String> array = new ArrayList<String>(); array.add("[1]明治大学"); array.add("[2]法政大学"); array.add("[3]早稲田大学"); array.add("[4]立教大学"); System.out.println("2019年ドラフト1位の森下選手の出身大学は?"); System.out.println(array); Integer number = new Scanner(System.in).nextInt(); if (number == 1) { System.out.println("正解です!"); } else { array.remove(number - 1); System.out.println("間違いです"); System.out.println("番号を選び直してください。"); System.out.println(array); } } }import java.util.ArrayList;
import java.util.List;
を記入していないとエラーが出るので注意したいと思います。ターミナル2019年ドラフト1位の森下選手の出身大学は? [[1]明治大学, [2]法政大学, [3]早稲田大学, [4]立教大学] 1 正解です!↑正解のパターン
ターミナル2019年ドラフト1位の森下選手の出身大学は? [[1]明治大学, [2]法政大学, [3]早稲田大学, [4]立教大学] 2 間違いです 番号を選び直してください。 [[1]明治大学, [3]早稲田大学, [4]立教大学]↑間違えたパターン
2の法政大学が表示されなくなりましたね。とりあえず今回の記事はこんな感じで終わります。
この機能の改善点
①[[1]明治大学, [2]法政大学, [3]早稲田大学, [4]立教大学]みたいにターミナルに配列の中身を表示させる時に[]を表示支えないようにする。
②選び直してください。の表示が出たら数字を入力できるようにする。多分繰り返し処理でできると思う。
③問題を増やす。
④問題をランダムで出題できるようにする。
⑤答えをランダム表示にできるようにする。
今思いつく限りではこのような感じですね。次回の記事で書いていきたいと思います。まだまだJavaのことはわかりませんので、頑張っていきたいと思います。
- 投稿日:2020-09-21T17:25:09+09:00
初めてのjava.util.logging
本記事の結論
- java.util.loggingとは、Javaに搭載されたロギングを担うモジュールのこと
ロギングとは
- 出来事を時系列に沿って記録した履歴のこと
- ログレベルが存在する
ログレベルとは
- 文字通りログのレベルのこと
- java.util.loggingでは、下記のようにレベル分けされている。なお、デフォルトでは、INFO以上のログが出力されるよう設定されている。
- FINEST - 非常に詳細なトレースメッセージ
- FINER - かなり詳細なトレースメッセージ
- FINE - 詳細なトレースメッセージ
- CONFIG - 静的な構成メッセージ
- INFO - 情報メッセージ
- WARNING - 警告メッセージ
- SEVERE - 重大なメッセージ
java.util.loggingの機能
- Logger - ログの出力
- Handler - ログの出力先を制御
- Formatter - ログのフォーマットを制御
参考
- 投稿日:2020-09-21T16:09:36+09:00
Spring Boot で Ajax を使用したファイルのアップロード、ダウンロード(JQuery未使用)
作るもの2つ
- Ajaxで画像をアップロードして、サーバーに格納後に表示する
- 表示する画像を選択して画像表示ボタンを押すと、Ajaxでダウンロードして画像が表示される
デモ画面
1. ファイルアップロード
- 画面側
imageajax.html(一部)<body> <h3>ファイルアップロード</h3> <form id="imageFormId"> <input type="file" name="inputFileName" /> <button type="button" onclick="uploadImage('imageFormId', 'myUploadedImageId')">画像アップロード</button> </form> <img id="myUploadedImageId" style="width: 400px"> <script> function uploadImage(imgForm, imgCtrl) { const formData = new FormData(document.getElementById(imgForm)); const request = new XMLHttpRequest(); request.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { const img = document.getElementById(imgCtrl); const url = window.URL || window.webkitURL; // レスポンスを画像表示する img.src = url.createObjectURL(this.response); } } request.open("POST", "/uploadimage"); request.responseType = 'blob'; request.send(formData); } </script> </body>
- コントローラ側
ImageAjaxController(一部)/** * ファイルアップロード */ @PostMapping("/uploadimage") public ResponseEntity<byte[]> uploadImage(@RequestParam("inputFileName") final MultipartFile uploadFile) { if (uploadFile.isEmpty()) { return ResponseEntity.of(Optional.empty()); } // アップロードされたファイルを格納するためのPathを取得 final Path path = Paths.get("c:/uploaddir", uploadFile.getOriginalFilename()); final byte[] bytes; try { // アップロードされたファイルのバイナリを取得 bytes = uploadFile.getBytes(); // ファイルの格納 Files.write(path, bytes); } catch (IOException e) { return ResponseEntity.of(Optional.empty()); } return ResponseEntity.ok(bytes); }
- アップロードするファイルは上限1MBなので、それ以上のサイズをアップロードするには、以下のように設定する
application.ymlspring: servlet: multipart: max-file-size: 30MB max-request-size: 30MB2. ファイル選択
- 画面側
imageajax.html(一部)<body> <h3>ファイル選択</h3> <select id="selectImageId"> <option value="a">a</option> <option value="b">b</option> </select> <button type="button" onclick="displayImage('selectImageId', 'mySelectedImageId')">画像表示</button> <img id="mySelectedImageId" style="width: 400px"> <script> function displayImage(selectCtrl, imgCtrl) { const select = document.getElementById(selectCtrl); const request = new XMLHttpRequest(); request.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { const img = document.getElementById(imgCtrl); const url = window.URL || window.webkitURL; // レスポンスを画像表示する img.src = url.createObjectURL(this.response); } } // 選択肢を送って、ファイルを取得する request.open("GET", "/selectimage/" + select.value); request.responseType = 'blob'; request.send(); } </script> </body>
- コントローラ側
ImageAjaxController(一部)/** * ファイル選択 */ @GetMapping("/selectimage/{selectNum}") public ResponseEntity<byte[]> getImage(@PathVariable final String selectNum) { // 選択肢からファイル名取得 final String fileName; switch (selectNum) { case "a": fileName = "a.jpg"; break; case "b": fileName = "b.jpg"; break; default: return ResponseEntity.of(Optional.empty()); } // ファイルのPath final Path path = Paths.get("c:/selectdir", fileName); final byte[] bytes; try { // ファイルを読み込む bytes = Files.readAllBytes(path); } catch (IOException e) { return ResponseEntity.of(Optional.empty()); } return ResponseEntity.ok(bytes); }
- 投稿日:2020-09-21T15:47:01+09:00
【java】【javaSliver】インタフェースと抽象クラスがややこしいのでまとめ
この記事の目的
来週javaシルバーを受験するので、曖昧な部分を潰していくことに。
ちょっと混乱していたインタフェース
と抽象クラス
について整理しておこうかな。というのがこの記事の目的です。混乱していた理由
「抽象メソッドはabstractをつけて宣言する。つけないとコンパイルエラー」と書かれていたり、
「省略した場合、暗黙的にpublic abstractが付与される」と書かれていたり・・・「あれ?勘違い?」と混乱していました。
ので、一旦しっかり整理して混乱から脱出したかったんです。
今思えば、抽象メソッドと抽象クラス、インタフェースがごっちゃになっていたんだろうな。って思ってます。ルール比較
インタフェースと抽象クラスで定義できるものを一覧でまとめました。整理整理。
メソッドとフィールド変数に分けて整理しました。メソッドについて
まずはメソッドから。
インタフェース
と抽象クラス
それぞれでテーブルにまとめました。インタフェースのメソッド達
定義できるもの アクセス修飾子 省略できる? その他 抽象メソッド public 省略してもpublic インタフェースだとabstractも省略できるよ。 defaultメソッド public 省略できちゃう ※SE8以降定義できるようになりました。 staticメソッド public or private 省略するとpublic ※SE8以降定義できるようになりました。 ポイント
- インタフェースの場合で宣言する抽象メソッドは
abstract修飾子
も省略できるみたいです。- アクセス修飾子を記述しないとpublicになるってのが共通ルールですかね?
- staticメソッドについては、アクセス修飾子がpublicに限らずprivateの指定もできちゃうのがちょっと厄介ですね。
なんで、インタフェースだとabstract修飾子を省略できるんだろ?
これは個人的にたどり着いた推測ですが・・・。
インタフェースでは基本的に抽象メソッドしか定義できない(SE8までは)ので、abstractを明示的に宣言しなくても、コンパイラが空気読んでabstractつけてくれるんだな。と考えることにしました。抽象クラスのメソッド達
定義できるもの アクセス修飾子 省略できる? その他 抽象メソッド なんでもあれ abstractは必須。 アクセス修飾子は省略可。省略してもpublicとかにはならないよ 具象メソッド なんでもあれ 省略できちゃう アクセス修飾子は省略可。省略してもpublicとかにはならないよ ポイント
- インタフェースと違って、アクセス修飾子は自由に定義できちゃう。
- アクセス修飾子を省略すると、具象クラスと一緒で、修飾子なしのメソッドとして扱っちゃう。
- 抽象メソッドの
abstract修飾子
は必須!!!なんで、抽象クラスだとabstract修飾子を省略できないんだろう?
抽象クラスの中には、具象メソッドと抽象メソッドが混在するから、混乱しないように明示的にabstractをつけないといけないって考えることにしました。
(これも個人的にたどり着いた推測です。)続いて、変数
インタフェース
フィールドの種類 定義できる?できない? staticを省略できる? その他 非static変数(インスタンス変数) 定義できない - インタフェースはインスタンス化できないからね。 static変数 定義できるけど、定数になるよ 省略できる 省略しても public static final
になっちゃう。ポイント
- インスタンス変数は定義できない
- static変数は何がなんでも
public stati final
になるインタフェースはインスタンス化できないから、インスタンスした時の変数のことなんて考える必要ないですね。
だから定義できないってことですね。うん。納得。
インスタンス化はできないけど、型としては扱えちゃうからこれまたややこしポイントですね。抽象クラス
フィールドの種類 定義できる?できない? staticを省略できる? その他 非static変数(インスタンス変数) 定義できる そもそもいらない アクセス修飾子はなんでも定義できちゃう。 static変数 定義できる 省略できない staticを省略したらインスタンス変数になってしまうよ。アクセス修飾子はなんでも定義できちゃう。 ポイント
- 具象メソッドとなんら変わらない!
まとめ
インタフェース
- 抽象メソッド → abstractは省略可能
- defaultメソッド
- staticメソッド → staticメソッドだけは privateでも扱える
- static変数 → static 変数は定数としてしか使えない
抽象クラス
- 抽象クラス → abstractは絶対書く
- 具象メソッド
- static変数
- 非static変数
さいごに
結構自分ようのメモになっちゃいましたが、やっぱりこうやって整理する時間は必要ですね。
もし、解釈が違う、間違っているなあればお手数をおかけしますが、ご指摘いただけますと幸いですm(_ _)m
- 投稿日:2020-09-21T15:05:07+09:00
swingにおける日本語入力時にUIが崩れる不具合と解決方法
はじめに
ここ最近、swing関連のバグ?に悩まされていました。
例えば、このようにJTextFieldやJTextAreaなどのJTextComponent系コンポーネントを用意したとします。
半角英数字を入力する分には特に変わりはないのですが、日本語入力をした途端に、バグにも程があります。
ググってみると
InputMethodListener
を用いてウィンドウのrepaint()
を叩き続ければ良いという解決法を見つけたので、フレームを最小化していない間はコンポーネントへの全角文字入力以降にwhile
でフレームのrepaint()
を叩き続けるためのバグ修正用クラスを用意。
バグ修正用クラス
final class SwingPaintBugFix extends Thread { private Window w; private boolean running = true; private static SwingPaintBugFix bugfix = new SwingPaintBugFix(); private SwingPaintBugFix() {} private SwingPaintBugFix(Window w) { this.w = w; } public static void of(JTextComponent txtComp, Window w) { w.addWindowListener(new WindowAdapter() { @Override public void windowIconified(WindowEvent e) { bugfix.stopRunning(); } @Override public void windowDeiconified(WindowEvent e) { bugfix = new SwingPaintBugFix(w); bugfix.start(); } }); txtComp.addInputMethodListener(new InputMethodListener() { @Override public void inputMethodTextChanged(InputMethodEvent e) { // 全角文字を受け取る bugfix.stopRunning(); bugfix = new SwingPaintBugFix(w); bugfix.start(); } @Override public void caretPositionChanged(InputMethodEvent e) {} }); txtComp.addKeyListener(new KeyAdapter() { // 半角文字を受け取る @Override public void keyTyped(KeyEvent e) {} }); } @Override public void start() { new Thread(this).start(); } @Override public void run() { while (running) { ((Component) w).repaint(); } } private void stopRunning() { running = false; } }しかしながら、常にwhileで処理を実行し続けるという行為に抵抗感が否めず、すべてのJTextComponent系コンポーネントにバグ修正用クラスを適用するのも手間がかかるため、抜本的な解決策を探ることとしました。
原因究明
とりあえず数回再起動しても治らず。
Windows Updateで不具合がでないかどうか確認しながら最新版に更新しても駄目。
「あーめんど」と思いながらも、仕方がないのでセーフモードに。セーフモードでは先程のバグが発生しないことを確認したので、「バッググラウンドプロセス」と「サービス」を控えて通常起動に戻す。
「これでもない、これでもない・・・」と、タスクマネージャーからセーフモード以外のタスクを一つづつ終了させ、ついに原因を特定!
「Nahimic Service」というサウンドユーティリティが犯人だったようです。こいつを試しに停止させてみたら見事にバグが直りました。ググってみると、こいつはUI関連以外にもいろいろと競合を起こしているようです。
・・・これならバグ修正クラスなんて作る意味なかったじゃんか。
- 投稿日:2020-09-21T12:45:27+09:00
【Java】文字列と文字列の比較をする時の注意点
結論:Javaで変数に格納した文字列を比較する時はequals()を使おう。
やりたい事
- String str1 = "hoge"
- String str2 = "hoge"
- str1とstr2が等しい時はif文の処理に入れる
参考
文字列と文字列の比較
https://www.javadrive.jp/start/string/index4.html【Java入門】文字列(String)を比較する方法(「==」と「equals」)
https://www.sejuku.net/blog/14621変数に数値を代入して比較する場合
int num = 10 if (num == 10) { System.out.println("numは10です。"); } else { System.out.println("numは10ではありません。"); }この式では、変数numに格納しているのは10なので処理結果は
numは10です。変数に文字列型を代入して比較する場合
String str1 = "hoge" String str2 = "hoge" if (str1 == str2) { System.out.println("str1とstr2は同じです。"); } else { System.out.println("str1とstr2は違います。"); }この式では変数str1と変数str2は同じ文字列hogeが格納されているので処理結果は「str1とstr2は同じです。」と返ってくると思っていました。
str1とstr2は違います。。。。!?ほげ!?なんで!?
ここでドツボにはまり、300回くらいデバックしました。文字列がメモリ上で格納されている場所が一緒かどうか比較するメソッド「==」
「==」で比較しても違う文字列として扱われてしまいます。これはJavaの参照といいます
文字列は生成されるとメモリ上に文字列を保存しておくための場所(領域)が確保されます。その確保した「場所」をhogeは保持しています
つまり値を変数に保存しているわけではなく、場所を覚えておきhogeを使う際にその場所をたどって値を引っ張り出してくるという仕組みになっています
この参照という仕組みがあるため「==」で比較した場合、文字列の値が一緒かどうかではなく文字列がメモリ上で格納されている場所が一緒かどうかという比較になります
同じメモリ領域に保存されていれば
true
そうでなければfalse
を返します
先程の式でいうと、str1とstr2は違うメモリ領域に保存されている為、false
を返しますequals()メソッドを使って文字列を比較する
String str1 = "hoge" String str2 = "hoge" if (str1.equals(str2)) { System.out.println("str1とstr2は同じです。"); } else { System.out.println("str1とstr2は違います。"); }処理結果
str1とstr2は同じです。想定していた結果が返ってきました。
ちなみにrubyで書くと「==」でも同じ結果が返ってきます
str1 = "hoge" str2 = "hoge" if str1 == str2 then puts "str1とstr2は同じです。" else puts "str1とstr2は違います。" end # => str1とstr2は同じです。まとめ
rubyやJavaScriptを使っている時は
数値型
も文字列型
も「==」を使って比較できるのでJavaでも疑いなくプログラムを書いていましたが、Javaは違うんですねー画面に穴が空くくらい文法ミスが無いかチェックしましたが、「参照」を知らないと私みたく余計な時間を使うので注意しましょう
おわり
- 投稿日:2020-09-21T12:41:08+09:00
離散対数問題 (Discrete Logarithm Problem) を任意 mod で解く
離散対数問題 (Discrete Logarithm Problem) を任意 mod で解く
離散対数問題 (Discrete Logarithm Problem) を解くアルゴリズムとして,Baby-step Giant-step を知っている方は多いと思います.しかし,任意 mod を取り扱っている記事がほとんど見当たらなかったので,その辺りを書いていきます.
まず,離散対数問題とは以下のような問題を指します.(Baby-step Giant-step による解法を知っている方は読み飛ばして下さい)
正整数 $M$ と $ 0 \leq X, Y \lt M $ なる整数 $X, Y$ が与えられる.
$$ X^K\equiv Y \pmod M \tag{1} $$ を満たす (最小の) 非負整数 $K$ を求めよ.M が素数の場合
以下のようにこの問題を解くことが出来ます.
まず,簡単な $X=0$ のケースを処理してしまいましょう.$Y=0$ なら $K=1$,$Y\neq 0$ なら $K=-1$ (存在しない) です.但し,ここでは $0^0=1$ としています.
続いて,$X\neq 0$ のケースを考えます.$p=\left\lceil\sqrt{M}\right\rceil$ として,$K=p*i+q\ (0\leq q\lt p)$ が $(1)$ を満たすような非負整数 $i, q$ を探索することにします.Fermat の小定理より,$0\leq i\lt p$ の範囲を調べれば十分です.
いま,$M$ が素数かつ $0\lt X\lt M$ なので $\gcd(X, M)=1$ が成り立ちます.従って,$X*X^{-1}\equiv 1 \pmod M$ を満たす $X^{-1}$ が $0\lt X^{-1} \lt M$ の範囲にただ一つだけ存在します.この $X^{-1}$ を用いて $(1)$ 式を以下のように変形します.
\begin{align} X^K&\equiv Y\pmod M\\ X^{p*i+q}&\equiv Y\pmod M\\ X^q&\equiv Y*A^i\pmod M\tag{2}\\ \end{align}2 行目から 3 行目の変形では $A=(X^{-1})^p$ と置きました.
$(2)$ の左辺がとりうる値は $X^0 \bmod M,X^1\bmod M,\dots,X^{p-1}\bmod M$ の高々 $p=\left\lceil\sqrt{M}\right\rceil$ 種類です.そこで,$X^j\bmod M\mapsto j$ に写すような連想配列 (
unordered_map
やHashMap
) を前もって計算しておくことにします.このとき,key
が既に挿入済みの場合はvalue
は上書きしないようにします.以上より,$(2)$ の右辺で $i=0,1,\dots,p-1$ を全て調べれば,目的の $i, q$ を得ることが出来ます (見つからなかった場合は $K=-1$).連想配列の
value
を上書きせず,また, $i$ を昇順に走査することで,最初に見つかった $i, q$ は $(1)$ を満たす最小の $K$ を構成することも分かります.
上の解法の計算量ですが,
- $p$ の計算は二分探索で $O(\log M)$
- $X^{-1}$ の計算はユークリッドの互除法で $O(\log M)$
- $A=(X^{-1})^p$ の計算は $O(p)=O(\sqrt{M})$
- 連想配列の前計算は $O(p)=O(\sqrt{M})$
- $i=0,\dots,p-1$ の全探索は $O(p)=O(\sqrt{M})$
より,全体 $O(\sqrt{M})$ です.
M が任意の場合
この記事の本題です.$M$ が合成数になると上のアルゴリズムをそのまま適用することは出来ません.$X^{-1}$ が存在しない可能性があるからです.しかし,$M$ が合成数の場合も ほとんど同じアルゴリズムで問題を解くことができます.
以下に解法を示します.
先に $M=1$ の場合を処理しておきます.この場合,任意の $X, Y$ に対して $K=0$ は $(1)$ を満たし,最小です.
以下,$M>1$ とします.
$g:=\gcd{(X,M)}$ とします.$g=1$ であれば,$X^{-1}$ が存在するので先に述べたアルゴリズムをそのまま適用することが出来るのでここで終了します.$g\neq 1$ だとそうはいかないので少し工夫します.
$X=g*X'$,$M=g*M'$ のように $X, M$ を積に分解します.
これらを $(1)$ に代入すると,$(g*X')^K\equiv Y \pmod{(g*M')}$ となります.従って,ある整数 $n$ が存在して,
$$(g*X')^K-Y=n*g*M'\tag{3}$$
が成立します.$K=0$ となるのは $Y=1$ の場合に限られるので,このケースも別で処理しておくことにします.$K>0$ のとき,$Y$ は $g$ の倍数である必要があるので,$Y$ も $Y=g*Y'$ のように分解しておきます ( $Y$ が $g$ の倍数でなければ,$(1)$ を満たす $K$ は存在しません ).$(3)$ 式の計算を進めることが出来て,式 $(4)$ を得ます.
\begin{align} g*X'*(g*X')^{K-1}-g*Y'&=n*g*M'\\ X'*(g*X')^{K-1}-Y'&=n*M'\\ X'*(g*X')^{K-1}&\equiv Y'\pmod{M'}\tag{4} \end{align}さらに,$\gcd{(X',M')}=1$ が成り立つので $X'*{X'}^{-1}\equiv 1 \pmod{M'}$,$0\lt {X'}^{-1}\lt M'$ なる ${X'}^{-1}$ がただ一つ存在します.これを用いることで $(5)$ 式を得ます.
\begin{align} X'*(g*X')^{K-1}&\equiv Y'\pmod{M'}\\ (g*X')^{K-1}&\equiv Y'*{X'}^{-1}\pmod{M'}\\ X^L&\equiv Z\pmod{M'}\tag{5}\\ \end{align}2 行目から 3 行目の変形では $L=K-1,\ Z=Y'*{X'}^{-1}$ と置きました.
$(5)$ は元の問題よりもサイズの小さい離散対数問題になっているので,再帰的にこの問題を解くことが出来ます.また,後述の計算量解析を考えるとこの再帰が必ず終了することも分かります.$(5)$ を満たす最小の非負整数 $L$ が存在すれば $L+1$ が答えとなり,$L$ が存在しなければ元の問題の解も存在しません.
一つ注意すべきなのは,$\gcd{(X,M')}=1$ とは限らないということです.
例えば $(X, M)=(20, 24)$ とすると,$g=\gcd{(X, M)}=4$ なので,$M'=M/g=6$ です.この時,$\gcd{(X,M')}=2\neq 1$ となっています.
この解法の計算量は
( $\gcd{(X,M)}=1$ となるまでの再帰の計算量 ) + ( Baby-step Giant-step の計算量 )
です.再帰する必要があるのは $\gcd{(X,M)}\neq 1$ の場合で,$(X, M)$ は $(X, M/\gcd{(X, M)})$ へと移ります.この時,$\gcd{(X, M)} >= 2$ なので再帰の度に $M$ は半分以下になります.従って,再帰の回数は $O(\log M)$ 回で,再帰の度にユークリッドの互除法を定数回だけ行うので再帰の計算量は $O((\log M)^2)$ となります.
以上より,計算量は全体で $O(\sqrt M)$ で任意 mod での離散対数問題を解くことが出来ました.
実装例
最後に,任意 mod での離散対数問題を解く
Java
プログラムを掲載して終わりにしたいと思います.verify に使った問題: Discrete Logarithm
提出: Discrete LogarithmDiscreteLogarithm.javapublic class DiscreteLogarithm { /** * 任意 mod で離散対数問題を解く.つまり,x^k=y mod m を満たす最小の非負整数 k を求める. * @param x log の底 * @param y {@code x} の冪 * @param m mod * @return x^k=y mod m を満たす最小の非負整数 k.存在しない場合は -1 を返す. */ public static long log(long x, long y, long m) { if ((x %= m) < 0) x += m; if ((y %= m) < 0) y += m; // m = 1 のケースを処理 if (m == 1) return 0; // y = 1 (k = 0) のケースを処理 if (y == 1) return 0; long[] gi = gcdInv(x, m); // g=gcd(x,m), ix*x=g mod m long g = gi[0], ix = gi[1]; if (g != 1) { // y は gcd(x, g) の倍数である必要がある if (y % g != 0) return -1; m /= g; y = (y / g) * ix; // 小さいサイズの問題を解く long log = log(x, y, m); // 小さいサイズの問題に解が存在しなければ,元の問題にも解は存在しない if (log < 0) return -1; // 小さいサイズの問題の答えを l とすると,元の問題の答えは l + 1 return log + 1; } // gcd(x, g) = 1 であれば Baby-step Giant-step で解ける return coprimeLog(x, y, m, ix); } /** * x と m が互いに素な場合に,Baby-step Giant-step により離散対数問題を解く. * @param x log の底 * @param y {@code x} の冪 * @param m mod * @param ix mod {@code m} での {@code x} の乗法逆元 x^{-1} * @return x^k=y mod m を満たす最小の非負整数 k.存在しない場合は -1 を返す. */ private static long coprimeLog(long x, long y, long m, long ix) { long p = ceilSqrt(m); java.util.HashMap<Long, Long> memo = new java.util.HashMap<>(); for (long i = 0, powx = 1; i < p; i++) { memo.putIfAbsent(powx, i); powx = powx * x % m; } long a = pow(ix, p, m); for (long i = 0, ypowa = y; i < p; i++) { long q = memo.getOrDefault(ypowa, -1l); if (q >= 0) return p * i + q; ypowa = ypowa * a % m; } return -1l; } /** * (x-1)^2 < n <= x^2 を満たす x を二分探索する. * @param n sqrt を取る整数 * @return (x-1)^2 < n <= x^2 を満たす x */ private static long ceilSqrt(long n) { long l = -1, r = n; while (r - l > 1) { long m = (r + l) >> 1; if (m * m >= n) r = m; else l = m; } return r; } /** * a^b mod m を二分累乗法により計算する. * @param a 底 * @param b 指数 * @param m mod * @return a^b mod m */ private static long pow(long a, long b, long m) { if (m == 1) return 0; if ((a %= m) < 0) a += m; long pow = 1; for (long p = a, c = 1; b > 0;) { long lsb = b & -b; while (lsb != c) { c <<= 1; p = (p * p) % m; } pow = (pow * p) % m; b ^= lsb; } return pow; } /** * g = gcd(x, m), a * x = g mod m を満たす組 (g, x) をユークリッドの互除法により計算する. * @param a * @param m mod * @return g = gcd(x, m), a * x = g mod m を満たす組 (g, x) */ private static long[] gcdInv(long a, long m) { if ((a %= m) < 0) a += m; if (a == 0) return new long[]{m, 0}; long s = m, t = a; long m0 = 0, m1 = 1; while (t > 0) { long u = s / t; s -= t * u; m0 -= m1 * u; long tmp; tmp = s; s = t; t = tmp; tmp = m0; m0 = m1; m1 = tmp; } if (m0 < 0) m0 += m / s; return new long[]{s, m0}; } }