きり丸の技術日記

技術検証したり、資格等をここに残していきます。

Javaでメルセンヌ数を求める

Javaでメルセンヌ数(2の冪-1)を求める方法を記載します。

メルセンヌ数についてはWikipediaを参考にしてください。

前提

  • Java
    • 21

対応

2の冪-1が整数となる値を求めるために次の式を使用します。

private static boolean isMersenne(int value) {
  return log2(value + 1) % 1 == 0;
}
public static double log2(int value) {
  return Math.log(value) / Math.log(2);
}

解説

2の冪-1 = メルセンヌ数ですので、式変形して2の冪=メルセンヌ数+1と変形できます。2の冪数の計算については、次の式で求められます。

log2(value + 1);

public static double log2(int value) {
  return Math.log(value) / Math.log(2);
}

あとは、求めた冪数が整数であることを求めます。今回は値 % 1 == 0で整数を求めました。

private static boolean isMersenne(int value) {
  return log2(value + 1) % 1 == 0;
}

メルセンヌ素数を求める

メルセンヌ数に条件を追加するとメルセンヌ素数を求められます。素数は次のメソッドで求められます。詳しい原理については他の方のブログを参考にしてください。

public static boolean isPrime(int value) {
  if (value <= 1) {
    return false;
  }
  if (value <= 3) {
    return true;
  }
  for (int i = 2; i <= Math.sqrt(value); i++) {
    if (value % i == 0) {
      return false;
    }
  }

  return true;
}

ソースコード

終わりに

メルセンヌ数も素数も普段求めないから頭が回らないですね。解く機会があったので今回のブログにしました。

類似情報

Javaで二進対数(log2x)を求める

Javaで用意されているMathクラスだと十進対数(logx)だけしかないので二進対数(log2x)で計算する方法をメモします。

前提

  • Java
    • 21

対応

十進対数から二進対数を割るだけです。

public static double log2(int value) {
  return Math.log(value) / Math.log(2);
}

ソースコード

終わりに

普段のアプリケーションで対数を使うことがないのでメモしました。

Javaのtransientで本来は関わりのないデータであることを強調する

本来の使い方とは異なります。黙って使うと怒られるかもしれないので、導入の際は念のため確認してください。


Javaにはtransientという修飾子があります。これを使うことでコードで表現できることが増えます。

環境

  • Java
    • 15

ユースケース

  • コードの表現力を増やす
    • 関係がないデータであることを表現する

Transientとは

Transientは、和名で一時的という意味を持ちます。

この意味から私は「処理のために一時的に持たせた値」であることを、表現しています。

本来のTransientの役割

transientがついているフィールドをシリアライズの対象から外します。

シリアライズとはデータを保存したり、送受信できる形に変換することを指します。

逆に保存したファイルや送受信したものを扱えるようにすることを、デシリアライズと言います。

コード

例えば、このようなデータがあります。

public class Base{
    private final String id;
    private final String currencyCode;

    public boolean is本則課税(String taxDiv){
        return "01".equals(taxDiv);
    }
}
public class CodeMaster{
    private final String id;
}

Baseクラスにはid, currencyCodeしか持っていません。

Baseクラスが持っていない税区分を元に判別する処理がある場合、毎回外から値を渡す必要があります。1回しか使用しない場合はそれでもいいでしょう。

しかし、親子関係を持っていて、算出するたびに税区分が必要な場合、毎回外から値を渡すのは混乱の元となります。

ですので、Baseクラスにセットしておき、後で使いまわすと非常に処理が見やすくなります。

public class Base{
    private final String id;
    private final String currencyCode;
    private String taxDiv;

    public boolean is本則課税(){
        return "01".equals(this.taxDiv);
    }
}
public class CodeMaster{
    private final String id;
}

ただし、この処理を行った場合、BaseクラスがtaxDivを持つ構造になってしまいます。

明確な関係性がある場合はいいのですが、極稀に曖昧な関係性しか持たない場合があります。

その場合は、transientを持たせることで、一時的な値であることを表現できます。

public class Base{
    private final String id;
    private final String currencyCode;
    private transient String taxDiv;

    public boolean is本則課税(){
        return "01".equals(this.taxDiv);
    }
}
public class CodeMaster{
    private final String id;
}

同様に、currencyCodeを元に検索したマスタを処理中に扱いたいことがあるでしょう。Baseドメインに持たせることで処理が分かりやすくなることがあります。

public class Base{
    private final String id;
    private final String currencyCode;
    private transient String taxDiv;
    private transient CodeMaster codeMaster;

    public boolean is本則課税(){
        return "01".equals(this.taxDiv);
    }
}
public class CodeMaster{
    private final String id;
}

注意点

本来の扱い方とは違います。ですので、理解せずに使用すると意図しない挙動をすることがあります。

SerializationUtils.cloneメソッドを使用するとクローン出来ますが、Transientを使用するとクローン出来ません。本来の使い方なので当たり前ですが。

LombokのtoBuilderクラスでデータをコピーする場合は、Transientがついてもコピーできます。

このように、意図せずに挙動が変わってしまう可能性があるので、検証する必要があります。

終わりに

知らないよりは知っていた方がいい、という意味を込めてこの記事を書きました。

ただ、下手に思考を使ってしまうので、1か月後の自分は大否定している可能性はあります。

インターフェースのList型で宣言するのか、具体的な型のArrayListで宣言するのかだと、前者の方が個人的には好きですしね。

Effective Javaでは特にTransientの使い方等も書いてなかったですし、1つの表現方法としては一考の余地があるのではないかと考えています。