Several implementations of Joseph rings

1. Concept

    Before getting to the point, let me explain what a Joseph ring is. The Joseph ring is an applied mathematical problem: n people (represented by the numbers 1, 2, 3...n) are known to sit around a round table. The person numbered k starts counting, and the person who counts to m is dequeued; the next person starts counting from 1, and the person who counts to m is dequeued again; repeat this pattern until around the round table of people are all listed. Usually when solving such problems, we number from 0 to n-1, and finally [1] result + 1 is the solution of the original problem.

Second, through the array loop implementation

    The most common idea is to pass an array, each element in the array is a person, and then loop through the array, and when the number of people in the array reaches m, mark it as eliminated. Only one person in the array is not eliminated until the end.

    So, first of all, we need a calculation method with two parameters, the total number and the number of eliminations.

private static Integer compute(Integer total, Integer keyNumber) {
}

    In the second step, we need an array of boolean values ​​with a length of total, the index of the array indicates the number of people, and the true and false of the element indicate whether the person is eliminated. At the beginning we need to set everyone to not be eliminated.

        /*开始时设置一个长度为总人数的数组,并将元素都设为true start*/
        Boolean[] peopleFlags = new Boolean[total];
        for (int i = 0; i < total; i++) {
            peopleFlags[i] = true;
        }
        /*开始时设置一个长度为总人数的数组,并将元素都设为true end*/

    Next in the third step, we need three variables:

        The first variable records how many people are left to be eliminated, and the initial value of this variable is the total number of people;

        The second variable records the number of counts, and returns to zero when this parameter is equal to the number of eliminations;

        The third parameter records the number of people counted at that time, and returns to zero when this parameter is equal to the total number of people (because it is a circle, so after the last number of people is finished, it is the turn of the first number of people)

        int peopleLeft = total; //剩下的人数
        int count = 0; //计数器,每过一个人加一,加到keyNumber时归零
        int index = 0; //标记从哪里开始

    The fourth step starts the loop calculation. First, determine whether the remaining number of people is greater than one. If it is greater than one, enter the loop and take the index. If the person has not been eliminated, the counter will be incremented by one. If it is equal to keyNumber, the person will be eliminated, otherwise it will be skipped. Counting continues, when index equals the total number of people

        while (peopleLeft > 1) {
            if (peopleFlags[index]) {
                //说明还没有被淘汰 计数器加1
                count++;

                if (count == keyNumber) {
                    count = 0; //计数器归0
                    peopleFlags[index] = false; //此人被淘汰
                    peopleLeft--;//未被淘汰的人数-1
                }
            }
            index++;

            //当当前人等于总人数时,则又从第一人开始计数
            if (index == total) {
                index = 0;
            }
        }

    Finally, after the calculation is over, there is only one element in the array that is true, and this is the person who was not eliminated in the end. Now we start to find out who won the game.

        //经过上面的循环,现在数组中被淘汰的人都标记为false,最后没被淘汰都人标记为true
        for (int j = 0; j < total; j++) {
            if (peopleFlags[j]) {
                return j + 1;
            }
        }
        return null;

    Let's verify the result:

    public static void main(String[] args) {
        int total = 10;
        int keyNumber = 3;
        Integer winner = compute(total, keyNumber);
        System.out.println(total + "个人围成一圈数数,数到" + keyNumber + "的被淘汰,最后剩下的是第" + winner + "个人。");
    }

    OK, the first method is successful.

Second, through the chain list

    The earliest I learned about this method was through the horse soldier Java teaching video. In fact, this method can make people better understand the idea of ​​object-oriented and doubly circular linked list.

    The first step is to abstract some objects. In the proposition of Joseph rings, there are two objects: people and rings. A person has several properties: number, person on the left and person on the right. Rings have several properties and methods: total people, first person, last person, adding and removing people.

    Well, in the second step, we start to implement our abstracted objects, first the People object:

public class People {
    private Integer id;
    //因为是一个圈子那么人就有左右两个属性,而且左右也都是人,所以明确定义其类型
    private People left;
    private People right;

    public People(Integer i) {
        this.id = i + 1;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public People getLeft() {
        return left;
    }

    public void setLeft(People left) {
        this.left = left;
    }

    public People getRight() {
        return right;
    }

    public void setRight(People right) {
        this.right = right;
    }
}

    Next is the Circle object:

public class Circle {
    private int total = 0;
    private People first = null;
    private People last = null;

    /**
     * 向环里添加人,将新人添加到链表的尾部
     *
     * @param newPeople 新加入都人
     */
    public void addPeople(People newPeople) {
        if (total <= 0) {
            //如果总数小于或等于零,说明这是环里一个人都没有,这是添加后第一个和最后一个都是自己
            first = newPeople;
            last = newPeople;
            //此时这个人都的左边和右边也都是自己
            newPeople.setLeft(newPeople);
            newPeople.setRight(newPeople);
        } else {
            //如果环里有人,则将新人加入到尾部,因为是尾部,所以操作的就是first和last
            last.setRight(newPeople);//将原先最后一个人的右边变为新人
            newPeople.setLeft(last);//将新人的左边设为原最后一个人也就是现在的倒数第二人
            newPeople.setRight(first);//将行人的右边设为第一个人
            first.setLeft(newPeople);//将第一人的左边变为新人
            last = newPeople;//将最有一个人设为新人
        }
        total++;
    }

    /**
     * 删除被淘汰的人
     *
     * @param deletePeople 需要删除的人
     */
    public void deletePeople(People deletePeople) {
        if (total <= 0) {
            return;
        } else if (total == 1) {
            //如果环中只有一个人,那么游戏结束,首尾都设为null
            first = null;
            last = null;
        } else {
            //环中的人大于一个,是开始删除操作
            if (deletePeople == first) {
                first = deletePeople.getRight(); //如果是第一个人,那么他右边都人就变成第一人
            } else if (deletePeople == last) {
                last = deletePeople.getLeft();   //相反,如果是最后一个人,那么他左边边都人就变成最后一人
            }

            //将删除的人左边的人的右边设为删除的人的右边的人,是不是有点儿绕,举个例子:
            //234,我们要删除3,那么将2的左边设为4。
            deletePeople.getLeft().setRight(deletePeople.getRight());
            //同理,将删除的人右边的人的左边设为删除人的左边的人。
            deletePeople.getRight().setLeft(deletePeople.getLeft());
        }
        total--;
    }

    public int getTotal() {
        return total;
    }

    public void setTotal(int total) {
        this.total = total;
    }

    public People getFirst() {
        return first;
    }

    public void setFirst(People first) {
        this.first = first;
    }

    public People getLast() {
        return last;
    }

    public void setLast(People last) {
        this.last = last;
    }
}

    Finally, we start the calculation, first add people to the ring, then get the first person for processing, after processing the first one, get the person after the first person to continue processing, and finally return to the processed ring.

    private static Circle compute(Integer total, Integer keyNumber){
        Circle circle = new Circle();
        for (int i = 0; i < total; i++) {
            People people = new People(i);
            circle.addPeople(people); //向环中添加人
        }

        Integer count = 0;//用来计数
        People people = circle.getFirst(); //先拿到第一个人
        while (circle.getTotal() > 1) {
            count++;
            if (count.equals(keyNumber)) {
                count = 0;
                circle.deletePeople(people);
            }
            people = people.getRight();//一个一个往后报数
        }
        return circle;
    }

    Let's take a look at the execution results:

   public static void main(String[] args) {
        int total = 10; //定义要添加的人数
        int keyNumber = 3; //数到3退出
        Circle circle = compute(total, keyNumber);
        System.out.println(total + "个人围成一圈数数,数到" + keyNumber + "的被淘汰,最后剩下的是第" + circle.getFirst().getId() + "个人。");
    }

    If you succeed, do you feel that your thinking is a bit confusing, but this is a typical object-oriented idea, which can be digested and digested more.

3. Java comes with linked list implementation (LinkedList)

    In fact, using the LinkedList that comes with Java can easily achieve the effect we want. Go directly to the code:

public class ThirdWay {
    public static void main(String[] args) {
        Integer total = 10;
        Integer keyNumber = 3;
        LinkedList<Integer> list = new LinkedList<>();
        for (int i = 0; i < total; i++) {
            list.addLast(i+1);
        }
        int index = 0;
        while (list.size() > 1) {
            for (int i = 1; i < keyNumber; i++) {
                if (index == list.size() - 1) {
                    index = 0;
                } else {
                    index++;
                }
            }
            list.remove(index);
        }
        System.out.println(total + "个人围成一圈数数,数到" + keyNumber + "的被淘汰,最后剩下的是第" + list.get(0) + "个人。");
    }
}

    Is it very simple? The basis for this is because of the particularity of the remove() method of the linked list. In the first round of the loop, the element with index=2 is 3, but after it finds the element to be deleted, the linked list size-1, this When index=2 points to the element with the original index=3, that is to say, the index does not need to be changed, which just meets our needs.

    There are other ways to implement the Joseph ring, but I have only learned these three ways to implement it. If I learn other methods to implement it in the future, I will come back to supplement it.

Attached source code address: https://gitee.com/jack90john/joseph

------------------------------------------------------------------------

Welcome to pay attention to my personal public account and push the latest articles

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324377633&siteId=291194637