约瑟夫问题 java 实现

约瑟夫问题

这是17世纪的法国数学家加斯帕在《数目的游戏问题》中讲的一个故事:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,数到第九个人就将他扔入大海。该人后面的人从1开始重新报数,如此循环进行直到仅余15个人为止。问怎样排法,才能使每次投入大海的都是非教徒。

思路
1. 先建立一个类,有 id 和 isRemove

class Person {
    int id;
    boolean isRemove;

    public Person(int id, boolean isRemove) {
        this.id = id;
        this.isRemove = isRemove;
    }

    public boolean isRemove() {
        return isRemove;
    }

    public void setRemove(boolean isRemove) {
        this.isRemove = isRemove;
    }
}
  1. 2.
class Circle {
    private ArrayList<Person> circle = new ArrayList<Person>();

    private int amount; // 一共多少人

    Circle(int amount) {
        this.amount = amount;
        for (int i = 0; i < amount; i++) {
            Person p = new Person(i + 1, false);
            circle.add(p);
        }
    }

    /**
     * 
     * @param index
     *            最开始扔人的位置,比如第9人
     * @param total
     *            共需要扔几次
     */
    public void getIndex(int index, int total) {

        // 起始位置
        int currentIndex = -1;

        for (int t = 0; t < total; t++) {
            int notRemove = 0;

            //本来是notRemove != 9,写死不好
            while (notRemove != index) {
                currentIndex++;
                // 或者用 currentIndex % amount 解决
                if (currentIndex == amount) {
                    currentIndex = 0;
                }
                if (!circle.get(currentIndex).isRemove) {
                    notRemove++;
                }
            }

            // 将扔的人设为 true
            Person p = circle.get(currentIndex);
            p.setRemove(true);
            circle.set(currentIndex, p);

            System.out.printf("第 %-2d 次仍的人id是%4d\n", t + 1, p.id);

        }

    }

}

代码建立一个容器来保存各个人,主要就是不移除这个人,而是把他状态设为setRemove(true),好处这样容器的大小保持不变。如果删掉这样人,容器大小每次减1,算起来麻烦


if (currentIndex == amount) {
    currentIndex = 0;
}

本来是用 currentIndex % amount 实现的,不过本代码都是+1,肯定会有 == amount的情况,用置为0更好

结果

1  次仍的人id92  次仍的人id183  次仍的人id274  次仍的人id65  次仍的人id166  次仍的人id267  次仍的人id78  次仍的人id199  次仍的人id3010 次仍的人id1211 次仍的人id2412 次仍的人id813 次仍的人id2214 次仍的人id515 次仍的人id23

优化

也可以用数组实现,下标和 boolean 正好模拟上面的 Person 类

static void other() {
    boolean[] usaJapa = new boolean[30];
    // 用类库初始化
    Arrays.fill(usaJapa, true);

    int leftCount = usaJapa.length;

    int countNum = 0;
    int index = 0;
    int i = 0;
    while (leftCount > 15) {
        if (usaJapa[index]) {
            countNum++;
        }

        if (countNum == 9) {
            countNum = 0;
            usaJapa[index] = false;
            leftCount--;
            System.out.printf("第 %-2d 次仍的人id是%4d\n", ++i, index + 1);

        }

        index++;
        if (index == usaJapa.length) {
            index = 0;
        }
    }

}

用链表实现,remove 其中的数据

class Circle {
    private LinkedList<Person> circle = new LinkedList<Person>();

    private int amount; // 一共多少人

    Circle(int amount) {
        this.amount = amount;
        for (int i = 0; i < amount; i++) {
            Person p = new Person(i + 1, false);
            circle.add(p);
        }
    }

    public void othergetIndex(int mark, int total) {
        // remain 是剩下的人数,total 是需要删除的人数
        int remain = amount;
        int count = 0;
        int current = 0;
        int i = 0;
        while (remain > amount - total) {
            count++;
            // 注意这人如果达到 mark,比如第9个人
            // 删掉第9个人后,再删第9个人,就是删原来的第10个人
            if (count == mark) {
                // remove 返回的是删除的 Person
                Person p = circle.remove(current);
                System.out.printf("第 %-2d 次仍的人id是%4d\n", ++i, p.id);
                count = 0;
                remain--;
            } else {
                current++;
            }
            if (current == amount - i) {
                current = 0;
            }

        }

    }
}

用 LinkedList 删除操作更快,

Person p = circle.remove(current);
remove 返回删除的元素

LinkedList<Integer> testList = new LinkedList<Integer>();
for (int i = 0; i < 10; i++) {
        testList.add(i);
}
for (int i = 0; i < 5; i++) {
    System.out.println(testList.remove(3));
}   /**
    *   3
    *   4
    *   5
    *   6
    *   7
     */

这段代码的输出可以看出,删除了第3个元素,后面元素往前移动

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值