乐趣区

关于敏捷:敏捷小游戏的思考上

最近,咱们团队为了进一步提高合作效率,组织了一次趣味分享流动,心愿通过游戏的形式,帮忙技术部门的共事们更好地了解和实际麻利开发的办法。

在这次流动中,“哪种口头形式更麻利”、“为什么采纳这种办法”等关乎麻利概念实践和实际办法的思考被搭档们提出来并公开探讨。作为开发小组的成员,我获益颇多,特将此次流动中的思考总结下来分享给大家,独特成长~

游戏规则

10 集体翻 30 张牌,每个人要把这 30 张牌的每 1 张牌都翻一遍,计算第 1 集体翻完 30 张牌所消耗的工夫和所有人翻完 30 张牌所消耗的工夫~

假如每一个红色方块耗时 x,每一个橙色方块耗时 y,每一个黄色方块耗时 z(x, y, z > 0)

计划 1: 第 n 集体必须等第 n - 1 集体翻完所有的 30 张牌能力开始翻牌,第 1 集体不必等

那么咱们参考上图的构造,先计算第 1~5 集体的翻完 30 张牌的工夫,而后再乘以 2 就是总计用时(不存在影响工夫的意外状况):

T1 = ((5 + 1) 5 / 2 x + (4 + 1) 4 / 2 y + 25 5 z) * 2 =30x + 20y + 250z

计划 2:第 n 集体必须等第 n - 1 集体翻完第 5 张牌能力开始翻牌,第 1 集体不必等;且第 n 集体必须等第 n - 1 集体翻完第 m 张牌,能力翻第 m 张牌,第 1 集体不必等。

参考上图构造,先计算 1 ~ 10 集体翻完前 5 张牌的工夫,而后再计算第 10 集体翻完 6 ~ 25 张牌的工夫就是总计用时(不存在影响工夫的意外状况):

T2= ((5 + 1) 5 / 2 x + (4 + 1) 4 / 2 y) 2 + 25 z = 30x + 20y + 25z

计划 3:第 n 集体必须等第 n - 1 集体翻完第 m 张牌,能力翻第 m 张牌,第 1 集体不必等。

参考上图构造:先计算 1 ~ 10 集体翻完第 1 张牌的工夫,而后再计算第 10 集体翻完第 2 ~ 5 张牌的工夫,最初再加上第 10 集体翻完第 6 ~ 30 张牌的工夫就是总计用时(不存在影响工夫的意外状况):T3= 10x + 4y + 25z

最终后果

上面咱们用程序来验证论断的正确性。
假如 x = y = z = 1s,那最终后果应该是:

T1 = 30 + 20 + 250 = 300s

T2 = 30 + 20 + 25 = 75s

T3 = 10 + 4 + 25 = 39s

理论后果:


public class AgileTest {private static final Logger LOGGER = LoggerFactory.getLogger(AgileTest.class);

    public static void main(String[] args) {long time1 = way1();
        long time2 = way2();
        long time3 = way3();
        LOGGER.info("time1: {} ms", time1);
        LOGGER.info("time2: {} ms", time2);
        LOGGER.info("time3: {} ms", time3);
    }

    /**
     * <p> 计划 1 </p>
     * <p> 第 n 集体必须等第 n - 1 集体翻完所有的 30 张牌能力开始翻牌,第 1 集体不必等。</p>
     * @return 耗时 ms
     */
    public static long way1() {
        final String way = "way1";
        long startTime = System.currentTimeMillis();
        // 1.10 集体
        for (int i = 1; i < 11; i++) {AtomicInteger atomicI = new AtomicInteger(i);
            // 2.30 张牌,每个人翻完 30 张牌,下集体能力开始
            for (int j = 1; j < 31; j++) {node(way, atomicI, j);
            }
        }
        long endTime = System.currentTimeMillis();
        return endTime - startTime;
    }

    /**
     * <p> 计划 2 </p>
     * <p> 第 n 集体必须等第 n - 1 集体翻完第 5 张牌能力开始翻牌,第 1 集体不必等;且第 n 集体必须等第 n - 1 集体翻完第 m 张牌,能力翻第 m 张牌,第 1 集体不必等。</p>
     * @return 耗时 ms
     */
    public static long way2() {
        final String way = "way2";
        long startTime = System.currentTimeMillis();
        CountDownLatch latch = new CountDownLatch(10);
        Map<Integer, Integer> map = new ConcurrentHashMap<>();
        // 1.10 集体
        for (int outUser = 1; outUser < 11; outUser++) {
            int outLastUser = outUser - 1;
            Integer lastOutUserNode = 0;
            // 2. 每个人必须等后面那个人翻完 5 张牌能力开始翻牌,第 1 集体例外
            while (outUser != 1 && ((lastOutUserNode = map.get(outLastUser)) == null || lastOutUserNode < 5)) { }
            AtomicInteger atomicI = new AtomicInteger(outUser);
            Thread thread = new Thread(() -> {
                try {int innerUser = atomicI.get();
                    int lastInnerUser = innerUser - 1;
                    // 3.30 张牌
                    for (int j = 1; j < 31; j++) {
                        // 4. 翻牌的速度不能超过前 1 集体,第 1 集体例外,第 30 张除外
                        Integer lastInnerUserNode = 0;
                        while (innerUser != 1 && ((lastInnerUserNode = map.get(lastInnerUser)) == null || lastInnerUserNode < j)) { }
                        node(way, atomicI, j);
                        map.put(innerUser, j);
                    }
                } catch (Exception e) {LOGGER.error("Current Thread:" + Thread.currentThread().getName() +  "+, exception:", e);
                } finally {latch.countDown();
                }
            }, "thread-user-" + outUser);
            thread.start();}

        try {latch.await();
        } catch (InterruptedException e) {LOGGER.error("wait thread exception:", e);
        }

        long endTime = System.currentTimeMillis();
        return endTime - startTime;
    }

    /**
     * <p> 计划 3 </p>
     * <p> 第 n 集体必须等第 n - 1 集体翻完第 m 张牌,能力翻第 m 张牌,第 1 集体不必等。</p>
     * @return 耗时 ms
     */
    public static long way3() {
        final String way = "way3";
        long startTime = System.currentTimeMillis();
        CountDownLatch latch = new CountDownLatch(10);
        Map<Integer, Integer> map = new ConcurrentHashMap<>();
        // 1.10 集体
        for (int outUser = 1; outUser < 11; outUser++) {AtomicInteger atomicI = new AtomicInteger(outUser);
            Thread thread = new Thread(() -> {
                try {int innerUser = atomicI.get();
                    int lastInnerUser = innerUser - 1;
                    // 2.30 张牌
                    for (int j = 1; j < 31; j++) {
                        // 3. 翻牌的速度不能超过前 1 集体,第 1 集体例外
                        Integer lastInnerUserNode = 0;
                        while (innerUser != 1 && ((lastInnerUserNode = map.get(lastInnerUser)) == null || lastInnerUserNode < j)) { }
                        node(way, atomicI, j);
                        map.put(innerUser, j);
                    }
                } catch (Exception e) {LOGGER.error("Current Thread:" + Thread.currentThread().getName() +  "+, exception:", e);
                } finally {latch.countDown();
                }
            }, "thread-user-" + outUser);
            thread.start();}

        try {latch.await();
        } catch (InterruptedException e) {LOGGER.error("wait thread exception:", e);
        }

        long endTime = System.currentTimeMillis();
        return endTime - startTime;
    }

    /**
     * 执行节点
     * @param atomicI 第 i 集体
     * @param j 第 j 张牌
     */
    private static void node(String way, AtomicInteger atomicI, int j) {int i = atomicI.get();
        long currentTime = System.currentTimeMillis() / 1000;
//        System.out.printf("[%s] %s i-j: %d-%d %d\n", Thread.currentThread().getName(), way, i, j, currentTime);
        try {if ((i >= 1 && i <= 5 && j >= 1 && j <= 5 && i + j <= 6)
                    || (i >= 6 && i <= 10 && j >= 1 && j <= 5 && i + j <= 11)) {Thread.sleep(1000);
            } else if ((i >= 1 && i <= 5 && j >= 1 && j <= 5 && i + j > 6)
                    || (i >= 6 && i <= 10 && j >= 1 && j <= 5 && i + j > 11)) {Thread.sleep(1000);
            } else if (j >= 6) {Thread.sleep(1000);
            }
        } catch (Exception e) {LOGGER.error("node sleep exception:", e);
        }
    }
}

日志输入:


17:52:10.645 [main] INFO com.peng.java_study.practice.zhikan.AgileTest - time1: 302746 ms
17:52:10.648 [main] INFO com.peng.java_study.practice.zhikan.AgileTest - time2: 75632 ms
17:52:10.648 [main] INFO com.peng.java_study.practice.zhikan.AgileTest - time3: 39348 ms

从日志中能够看出,理论后果与预期基本一致,多进去的几百毫秒是程序在运行过程中不可避免的耗费。由它们耗时可知:T3 < T2 < T1,所以在不思考其余因素影响的前提下,计划 3 是最麻利的!

深刻思考

在任何条件下,计划 3 都实用吗?

答案是否定的,在某些非凡状况下,计划 3 反而更慢!

假如:

第 n – 1 集体翻完 1 ~ 30 张牌后,一起交接给第 n 集体的筹备工夫和沟通工夫为 a。那么最终耗时为:T1’= T1 + (10 – 1) * a =T1 + 9a

若第 n – 1 集体翻完第 1 ~ 5 张牌后,一起交接给第 n 集体时,须要筹备工夫和沟通工夫为 b;第 n – 1 集体翻完第 6 ~ 30 张牌中的任意一张牌后,交接给第 n 集体时,须要的筹备工夫与沟通工夫 c。那么最终耗时为:T2’= T2 + (10 – 1) b + 25 9 * c =T2 + 9b + 225c

第 n – 1 集体翻完 1 ~ 30 张牌中的任意一张牌后,交接给第 n 集体时,须要的筹备工夫与沟通工夫为 d。那么最终耗时为:T3’= T_3 + 30 9 d = T3 + 270d

当初,咱们来验算一遍:

当「筹备工夫与沟通工夫」与「单个工作的执行工夫时」的大小满足肯定的条件时,计划 3 是否有可能比计划 1 慢?

假如T3’> T1’,那么:

T3 + 270d > T1 + 9a 即 10x + 4y + 25z + 270d > 30x + 20y + 250z + 9a

再次假如 x = y = z = 1s,那么上式就等同于:

10 + 4 + 25 + 270d > 30 + 20 + 250 + 9a 即

d > (261 + 9a) / 270 或 a < (270d – 261) / 9

如果 d = 1s,那 a < 1s 就能够使计划 1 快于计划 3…

由此能够得出结论:在肯定条件下,计划 3 未必是最优的,且这种状况很有可能产生…

最终论断

在大多数状况下,计划 3 会比计划 1 更麻利,但在上述两大节中的非凡状况下,计划 3 反而是最慢的。因而在实在的生存场景中,还是 要“就地取材”,不能一概而论!

思考到这里并没有进行,更多探讨和分享能够期待“麻利小游戏的思考[下]”。
————————————————
LigaAI 新一代智能研发合作平台 让 AI 为您的研发团队提供个性化、智能化的我的项目合作体验,化繁就简,帮忙开发者专一、高效的创作。

退出移动版