質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.22%

図形配列をシャッフルして別の配列に格納する方法

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 394

kotton92

score 2

前提・実現したいこと

Figure型の配列figures1,2を用意します。
figures1の配列の中身を重複なくランダムにシャッフルしたものをfigures2に格納し、figure2に格納された図形をそれぞれ
図形:面積
という形で出力したいです。

また、可能であればRectangle1,Rectangle2,Circle1,Circle2という風に図形に番号を割り当てたいと考えております。

求める出力結果

Rectangle1:200
Circle2:78.5
Circle1:1962.5
Rectangle2:600

ここで書いたRectangle,Circle,Circle,Rectangleという順番はあくまで一例なので子の通りでなく、他の順番で出力されてもかまいません。

以下のコードを書いて実行すると、

temp=figures1[index];


のところで、デバッグされ実行できなくなります。

原因が分かる方いらっしゃいましたら、教えていただけると幸いです。
どうぞよろしくお願いいたします。

ソースコード

import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;

public class Figures {

    public static void main(String[] args) {
        Figure [] figures1 = new Figure[4];
        Figure [] figures2 = new Figure[];
        Figure temp;

        Rectangle r1 = new Rectangle();
        Rectangle r2 = new Rectangle(30,20);
        int i = 0;

        figures1[i++]=r1;
        figures1[i++]=r2;

        Circle c1=new Circle();
        Circle c2=new Circle(25);
        figures1[i++]=c1;
        figures1[i++]=c2;


//ここからのコードの理解が浅いので、間違っている可能性が高いです。
        Random rnd= ThreadLocalRandom.current();
        for(int k=figures1.length-1;k>0;k--) {
            int index=rnd.nextInt(i+1);
            temp = figures1[index];
            figures1[index]=figures1[k];
            figures1[k]=temp;
                }

         for(i=0;i<figures2.length;i++) {


System.out.println(figures2[i].getClass().getName()+":"+figures2[i].getArea());
        }
}


class Figure{
        static int count = 0;
        double area =0;
        double getArea() {
            return 0;
        }
}
class Rectangle extends Figure{

        int width;
        int height;
        String name="Rectangle";

        Rectangle(){
            setSize(10,20);
        }

        Rectangle(int width,int height){
            setSize(width,height);
        }

        void setSize(int w,int h) {
            width=w;
            height=h;
        }

        double getArea() {
            return width*height;
        }
}
class Circle extends Figure{
        int r;
        String name="Circle";
        Circle(){
            setSize(5);
        }
        Circle(int r){
            setSize(r);
        }

        void setSize(int r){
            this.r=r;
        }
        double getArea() {
            return r*r*Math.PI;
        }
}

借用したコード

上記にて理解が浅いと記載した部分のコードにつきましては、以下のwebページから借用いたしました。
https://blog.y-yuki.net/entry/2018/08/22/094000

import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;

public class FisherYatesShuffleSample {

    public static void main(String[] args) {
        int[] targetArray = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9};

        // シャッフルする前
        printArray(targetArray, "No Shuffle");

        // シャッフル1回目
        shuffle(targetArray);
        printArray(targetArray, "Shuffle 1 ");

        // シャッフル2回目
        shuffle(targetArray);
        printArray(targetArray, "Shuffle 2 ");

        // シャッフル3回目
        shuffle(targetArray);
        printArray(targetArray, "Shuffle 3 ");
    }

    public static void shuffle(int[] array) {
        // 配列が空か1要素ならシャッフルしようがないのので、そのままreturn
        if (array.length <= 1) {
            return;
        }

        // Fisher–Yates shuffle
        Random rnd = ThreadLocalRandom.current();
        for (int i = array.length - 1; i > 0; i--) {
            int index = rnd.nextInt(i + 1);
            // 要素入れ替え(swap)
            int tmp = array[index];
            array[index] = array[i];
            array[i] = tmp;
        }
    }

    private static void printArray(int[] array, String headerComment) {
        System.out.printf("%s -> %s\n\n", headerComment, Arrays.toString(array));
    }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • kotton92

    2020/11/26 10:34 編集

    @dodox86さん
    https://blog.y-yuki.net/entry/2018/08/22/094000
    というページから借用したものになります。
    記載が不十分であり、大変失礼いたしました。

    キャンセル

  • kotton92

    2020/11/26 10:34

    @xebmeさん
    承知しました。
    ご丁寧にありがとうございます。

    キャンセル

  • xebme

    2020/11/26 11:01 編集

    要点は2つ。
    figures2が初期化されていないのは気づいていますか?
    スワップしないでfigures1の要素をfigures2に移送することに気づいていますか?iをkに置き換えたのは移送したいからでしょう。
    <--- いい加減なことを言っているかもしれない。回答します。

    キャンセル

回答 2

checkベストアンサー

+1

figure1/figure2の仮定

figure2のインスタンスを用意しているので、figure1はシャッフルしないで元のまま残す、と仮定します。


figure2

figure2のインスタンスをどうするか?figure1の要素を全て設定した後で複製するのが良いでしょう。

Figure[] figures2 = figures1.clone();

あとはお手本に従ってfigures2をシャッフルしてください。

シャッフルのバグ

シャッフルのループはiをkに変えたのだから全てkに変えてください。

int index=rnd.nextInt(k+1);

メソッド

複製とシャッフルを行うメソッドを書くとすれば次のようになります。

  static Figure[] cloneShuffle(Figure[] figures) {
    Figure[] result = figures.clone();
    Random rnd = ThreadLocalRandom.current();
    for (int k = result.length - 1; k > 0; k--) {
      int index = rnd.nextInt(k + 1);
      Figure temp = result[index];
      result[index] = result[k];
      result[k] = temp;
    }
    return result;
  }

シャッフルに型は関係ないから型変数を使うと次のようになります。

  static Figure[] cloneShuffle__(Figure[] figures) {
    return cloneShuffle_(figures);
  }

  static <T> T[] cloneShuffle_(T[] figures) {
    T[] result = figures.clone();
    Random rnd = ThreadLocalRandom.current();
    for (int k = result.length - 1; k > 0; k--) {
      int index = rnd.nextInt(k + 1);
      T temp = result[index];
      result[index] = result[k];
      result[k] = temp;
    }
    return result;
  }

シャッフル別解

質問に対する[追記・修正依頼]で指摘した方法です。これは難しくなります。バグがあるかもしれません。

figure2の初期化。

Figure [] figures2 = new Figure[figures1.length];

シャッフル。

  Random rnd = ThreadLocalRandom.current();
  for (int k = figures1.length; k > 0;) {
    int index = rnd.nextInt(k);
    temp = (figures2[index] == null) ? figures1[index] : figures2[index];
    if (index != --k) {
      figures2[index] = (figures2[k] == null) ? figures1[k] : figures2[k];
    }
    figures2[k] = temp;
  }

クラス名にインスタンス番号をつける

カウンターをスレッドセーフにするためにjava.util.concurrent.atomic.AtomicIntegerを使います。int型のcounterを使用すると++counterは2命令になりアトミックではないため。クラス構成にかなり問題がありましたので大幅に手直ししました。

abstract class Figure {

  final String name;

  public Figure(String name) {
    this.name = name;
  }

  abstract double getArea();

  @Override
  public String toString() {
    return name + ":" + String.format("%.1f", getArea());
  }

}

Rectangleクラス

class Rectangle extends Figure {

  static final String NAME = Rectangle.class.getSimpleName();
  static final java.util.concurrent.atomic.AtomicInteger counter = new AtomicInteger(0);
  final int width;
  final int height;

  Rectangle() {
    this(10, 20);
  }

  Rectangle(int width, int height) {
    super(NAME + counter.addAndGet(1));
    this.width = width;
    this.height = height;
  }

  @Override
  double getArea() {
    return width * height;
  }

}

Circleも同様です。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/11/26 16:18

    setterは危険なので意図的に削除しました。コンストラクターの中からオーバーライドされる可能性のあるメソッドは呼んではならない。コンストラクターから呼ぶメソッドはfinalかprivateにすること。

    キャンセル

  • 2020/11/28 14:39

    分かりました。
    ご丁寧に教えていただきありがとうございます。

    キャンセル

+1

次のコードは理解できますか?
これを参考に自分のコードを書いてみてください。

import java.util.Random;

public class Figures {
    public static void main(String[] args) {
        Figure[] fig1 = {
            new Rectangle(), new Rectangle(30, 20), new Circle(), new Circle(25)
        };
        Figure[] fig2 = {
            fig1[0], fig1[1], fig1[2], fig1[3] 
        };
        Random rnd = new Random();
        for (int i = 0; i < fig2.length; i++) {
            int j = rnd.nextInt(fig2.length);
            Figure t = fig2[i]; fig2[i] = fig2[j]; fig2[j] = t;
        }
        for (int i = 0; i <fig2.length; i++)
            System.out.println(fig2[i].getName() + ": " + fig2[i].getArea());
    }
}

class Figure {
    static int count = 0;
    double getArea() { return 0; }
    String getName() { return "Figure"; }
}

class Rectangle extends Figure {
    int width;
    int height;
    String name = "Rectangle_" + ++count;

    Rectangle() { setSize(10, 20); }
    Rectangle(int width, int height) { setSize(width, height); } 
    void setSize(int w, int h) { width = w; height = h; }
    double getArea() { return width * height; }
    String getName() { return name; }
}

class Circle extends Figure {
    int r;
    String name = "Circle_" + ++count;

    Circle() { setSize(5); }
    Circle(int r) { setSize(r); }
    void setSize(int r) { this.r = r; }
    double getArea() { return r * r * Math.PI; }
    String getName() { return name; }
}


static int count = 0; がどのように使用されているか、
String と int に +演算子を適用するとどうなるか、
getName() がどういう働きをするのか、などが理解できますか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/11/28 14:39

    お返事遅くなってしまい申し訳ございません。
    ありがとうございます。参考にさせていただきます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.22%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る