最近,咱们团队为了进一步提高合作效率,组织了一次趣味分享流动,心愿通过游戏的形式,帮忙技术部门的共事们更好地了解和实际麻利开发的办法。
在这次流动中,“哪种口头形式更麻利”、“为什么采纳这种办法”等关乎麻利概念实践和实际办法的思考被搭档们提出来并公开探讨。作为开发小组的成员,我获益颇多,特将此次流动中的思考总结下来分享给大家,独特成长~
游戏规则
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 为您的研发团队提供个性化、智能化的我的项目合作体验,化繁就简,帮忙开发者专一、高效的创作。