3
1

すべて計算すればいい、そう思ってた時期もありました

如何に楽にするために効率よくナニカするということを最大目標としているIT屋が、全パターン計算するとか無駄の極みだろ。
誰だよ、そんなクソみたいな実装してるやつ(超特大ブーメラン
参照:【脳筋プレー】JavaScript版

チェック処理とかは端折ってる

/** ここから定型文 */
ここで記載したクラスについては、
こっちの記事に書いてある、標準入力をList化したりしてるMainクラスから呼び出す予定だし・なんか継承したりしてるYO!!

ちな、Mainを実行して標準入力から入力するのクソ面倒くさいので、
Java編については問題の入力例をパラメータにしたテストクラスとかも作って公開するYO!!

開発・実行環境はこんな感じ
  • VSCode
  • Java 17
  • jUnit 5.9
  • maven

pom.xmlはこんな感じ

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>jp.co.asil</groupId>
    <artifactId>paiza202408</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.9.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>
/** ここまで定型文 */

実装クラス

Mod7.java
package jp.co.asil.paiza202408;

import java.util.List;

public class Mod7 extends Question {

  private int[] nums;
  private int[] modCount = new int[7];

  public Mod7(List<String> list) {
    super(list);
    this.nums = list.subList(1, list.size()).stream().mapToInt(Integer::parseInt).toArray();
    for (int num : nums)
      modCount[num % 7]++;
  }

  @Override
  public List<String> answer() {

    int count = 0;
    // あまり足して0 or 7の倍数になるケース
    // 000
    if (modCount[0] >= 3)
      count += (modCount[0] * (modCount[0] - 1) * (modCount[0] - 2)) / 6;

    for (int i = 1; i <= 3; i++) {
      // 115, 223, 331
      if (modCount[i] >= 2)
        count += (modCount[i] * (modCount[i] - 1) * (modCount[7 - (2 * i)])) / 2;
      // 446, 554, 662
      if (modCount[3 + i] >= 2)
        count += (modCount[3 + i] * (modCount[3 + i] - 1) * (modCount[8 - (2 * i)])) / 2;
      // 016, 025, 034
      count += (modCount[i] * modCount[7 - i] * modCount[0]);
    }

    // 124
    count += (modCount[1] * modCount[2] * modCount[4]);
    // 356
    count += (modCount[3] * modCount[5] * modCount[6]);

    return List.of(String.valueOf(count));
  }

}

テストクラス

Mod7Test.java
package jp.co.asil.paiza202408;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import org.junit.jupiter.api.Test;

public class Mod7Test {
  @Test
  void testAnswer1() {
    Mod7 testClass = new Mod7(List.of(
        "3",
        "10",
        "4",
        "14"));
    assertArrayEquals(testClass.answer().toArray(), new String[] { "1" });
  }

  @Test
  void testAnswer2() {
    Mod7 testClass = new Mod7(List.of(
        "10",
        "1",
        "2",
        "3",
        "4",
        "5",
        "6",
        "7",
        "8",
        "9",
        "10"));
    assertArrayEquals(testClass.answer().toArray(), new String[] { "17" });
  }

  @Test
  void testAnswer3() {
    List<String> param = new ArrayList<>();
    param.add("10000");
    param.addAll(IntStream.range(1, 10001).mapToObj(String::valueOf).collect(Collectors.toList()));
    Mod7 testClass = new Mod7(param);
    assertArrayEquals(testClass.answer().toArray(), new String[] { "1611717067" });
  }
}

7で割り切れるということは。。。

3つの数字を7で割った時の余りを足した時に以下のようなパターンになればいいんだ!

0 or 7の倍数その組み合わせは。。。

000

115 -- 511, 151
223 -- 322, 232
331 -- 133, 313
446 -- 644, 464
554 -- 455, 545
662 -- 266, 626

016 -- 061, 106, 160, 601, 610
025 -- 205, 052, 250, 502, 520
034 -- 043, 304, 340, 403, 430

124 -- 142, 214, 241, 412, 421
356 -- 365, 536, 563, 635, 653

7, 14, 21などの余りが000パターンの場合

実数(7, 14, 21)を使って順列で表すと以下の6パターンが考えられる。

  • 7 + 14 + 21
  • 7 + 21 + 14
  • 14 + 7 + 21
  • 14 + 21 + 7
  • 21 + 7 + 14
  • 21 + 14 + 7

が、今回求めるのは組み合わせなので、これらは1パターンとして集計するために、3!(3の階乗:3*2*1)で割ることに。

■ソースだと以下
count += (modCount[0] * (modCount[0] - 1) * (modCount[0] - 2)) / 6;

XXYの余りに同じ数字が2つ有るパターンの場合

例)1,5,8の115パターン
ここも上と同じ考え方だが、こっちは重複してる数が2つなので2!(2の階乗:2*1)で割ることに。

■ソースだと以下
count += (modCount[i] * (modCount[i] - 1) * (modCount[7 - (2 * i)])) / 2;

count += (modCount[3 + i] * (modCount[3 + i] - 1) * (modCount[8 - (2 * i)])) / 2;

XYZの同じ数字こないパターン

例)1,6,7の016パターン
ココは特に重複してる数がないので、そのまま集計する

■ソースだと以下
count += (modCount[i] * modCount[7 - i] * modCount[0]);

count += (modCount[1] * modCount[2] * modCount[4]);

count += (modCount[3] * modCount[5] * modCount[6]);

コード量を減らすために。。。

これらのパターンを1つ1つ丁寧に書いていってもいいけど、どうせなら見栄えとかも少し良くしたいよねってことで、規則性を探してループで回せそうなところは回す

1~3のループを回せば計算できそうな部分

115, 223, 331(XXYパターン1)

XXYって【i : i : 7 - (2 * i)】の組み合わせじゃね?

446, 554, 662(XXYパターン2)

XXYって【3 + i : 3 + i : 8 - (2 * i)】の組み合わせじゃね?

016, 025, 034(XYZパターン)

XYZって【0 : i : 7 - i】じゃね?

ソレ以外はベタで書いた

まとめ

パラメータを全部組み合わせで足して割ってってやるほうがコード量的には少ないけど、
最大条件の10000とか来ると10000*9999*9998回ループを回すのでパフォーマンス激落ちくんになると思われる。

このソースに限らず仕事で書くプログラムにも言えることかもしれませんが、新人レベルでも読める保守しやすいコードを書くか、処理速度を優先するために前提知識が必要なゴリゴリなコードを書くかは、プロジェクトのトレードオフな部分はあるよなぁ。。。

ある程度知識と経験が増えていくと、この案件は人が集めにくいから・新人の勉強用案件とか外部要因によって、コードの質(可読性重視 or 速度重視)を絶妙に調整しないといけなかったりするため、色々と気を使うよねぇ。。。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1