发表于: 2019-03-08 20:47:57

1 696



今天完成的事情:

1、今天就任务二查阅了很多资料,也看了一些师兄师姐们写的代码,同时自己上手写了一些demo。

2、通过师兄师姐的代码以及自己今天所学到的一些内容,写出一些js代码,感觉还有很多不足之处,需要改正。

以下是部分js代码:

var people;
var line;
var str="";
sessionStorage.clear()
//输入框滑块之间改变
function changeOne(){
people=document.getElementById('people');
   line=document.getElementById("Slider");
   line.value=people.value;
}
function changeTwo(){
people=document.getElementById('people');
   line=document.getElementById("Slider");
   people.value=line.value;

}
//点击减少人数
function minus(){
line=document.getElementById("Slider").value;
   if (line==4) {
return false;
   }else{
line--;
   }

document.getElementById("Slider").value=line;
   document.getElementById('people').value=line;

}
//点击增加人数
function add(){
line=document.getElementById("Slider").value;
   if (line==18) {
return false;
   }else{
line++;
   }

document.getElementById("Slider").value=line;
   document.getElementById('people').value=line;
}
//输入框
function over(){
people=document.getElementById('people').value;
   if (people>18) {
alert("请输入4-18个人数");
   }else if(people<4){
alert("请输入4-18个人数");
   }
}
//人数分配
function setting(){
people=document.getElementById('people');
   var myPeople=parseInt(people.value);
   var oUl=document.getElementById("oUl");
   var oLi=oUl.getElementsByTagName("li");
   str="";
   var TotalArr=new Array();
   var killer=Math.floor(myPeople/3);
   var number=new Array();
   //遍历所有为平民
   for (var n = 0; n < myPeople; n++) {
TotalArr[n]="平民";
   }
//抽取杀手
   for (var j = 0; j < killer; j++) {
number[j]=Math.floor(Math.random()*myPeople);
       TotalArr[number[j]]="杀手";
       //去重
   }
//判断显示结果
   for (var i = 0; i <myPeople; i++) {
if (TotalArr[i]=="杀手") {
str+='<li>'+'<div class="color-killer">'+'</div>'+'<p>'+(i+1)+'号'+TotalArr[i]+'</p>'+
'</li>';
       }else{
str+='<li>'+'<div class="color">'+'</div>'+'<p>'+(i+1)+'号'+TotalArr[i]+'</p>'+
'</li>';
       }
oUl.innerHTML=str;

       arr=JSON.stringify(TotalArr);
       sessionStorage.players=arr;
   }

}
window.onload=function(){
document.getElementById("next").onclick=function(){
if(str==''){
alert("请分配身份");
       }else{
location.href="js2-3.html";
       }
}
}


以下是今天所写的html代码:

<!DOCTYPE html>
<html>
<meta charset="utf-8">
<meta name="viewport" content="width=divice-width,initial-scale=1.0,user-scalable=no">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<head>
   <title>配比页面</title>
   <link rel="stylesheet" type="text/css" href="../css/js2.2.css">
   <link rel="stylesheet" type="text/css" href="css/reset.css">
   <link rel="stylesheet" type="text/css" href="css/public.css">
   <script src="../js/js2.2.js"></script>
</head>
<body>
<header>
   <img src="images/js2-1.png" class="h-img1" onclick="window.location='js2-1.html'">
   <h1>参数设置</h1>
   <img src="images/js2-2.png" class="h-img2">
</header>
<div class="container">
   <div class="player">
       <div class="player-1">
           <p>玩家配比</p>
       </div>
       <div class="player-2">
           <ul id="oUl">

           </ul>
           <p class="set" onclick="setting()">点击设置<img src="images/js2-3.png"></p>
       </div>
   </div>
   <div class="word">
       <input type="text" placeholder="平民词组">
       <input type="text" placeholder="幽灵词组">
   </div>
   <div class="people">
       <p>玩家人数<input id="people" value="8" max-length="2" onchange="changeOne();over()"></p>
       <div class="p-span1"><span onclick="minus()" >-</span></div>
       <input class="line" id="Slider" type="range" value="8" min="4" max="18" oninput="changeTwo()">
       <div class="p-span2"><span onclick="add()" >+</span ></div>
   </div>
   <button id="next">去发牌</button>
</div>
</body>
</html>


明天计划的事情:

1、计划再用两至三天的时间完成js任务2-4。

2、明天重点学习一下if的用法和人数分配的方法。


遇到的问题:

1、首先感觉自己在js方面还有很多不熟的地方,需要学习的也很多,写代码的时候经常会有无从下手的感觉。

2、在人数分配方面不知道该怎么弄,之后在师兄的指导下找到了思路,知道使用if来实现人数分配,也可以用if语句嵌套for循环来判断


收获:

splice

几年前楼主还真碰到过洗牌问题,还真的是 "洗牌"。当时是用 cocos2d-js(那时还叫 cocos2d-html5)做牌类游戏,发牌前毫无疑问需要洗牌。

当时我是这样做的。每次 random 一个下标,看看这个元素有没有被选过,如果被选过了,继续 random,如果没有,将其标记,然后存入返回数组,直到所有元素都被标记了。后来经同事指导,每次选中后,可以直接从数组中删除,无需标记了,于是得到下面的代码。

function shuffle(a) {  var b = [];  while (a.length) {    var index = ~~(Math.random() * a.length);
    b.push(a[index]);
    a.splice(index, 1);
  }  return b;
}

这个解法的正确性应该是没有问题的(有兴趣的可以自己去证明下)。我们假设数组的元素为 0 - 10,对其乱序 N 次,那么每个位置上的结果加起来的平均值理论上应该接近 (0 + 10) / 2 = 5,且 N 越大,越接近 5。为了能有个直观的视觉感受,我们假设乱序 1w 次,并且将结果做成了图表,猛戳 http://hanzichi.github.io/test-case/shuffle/splice/ 查看,结果还是很乐观的。

验证了正确性,还要关心一下它的复杂度。由于程序中用了 splice,如果把 splice 的复杂度看成是 O(n),那么整个程序的复杂度是 O(n^2)。

Math.random()

另一个为人津津乐道的方法是 "巧妙应用" JavaScript 中的 Math.random() 函数。

function shuffle(a) {  return a.concat().sort(function(a, b) {    return Math.random() - 0.5;
  });
}

同样是 [0, 1, 2 ... 10] 作为初始值,同样跑了 1w 组 case,结果请猛戳 http://hanzichi.github.io/test-case/shuffle/Math.random/

看平均值的图表,很明显可以看到曲线浮动,而且多次刷新,折现的大致走向一致,平均值更是在 5 上下 0.4 的区间浮动。如果我们将 [0, 1, 2 .. 9] 作为初始数组,可以看到更加明显不符预期的结果(有兴趣的可以自己去试下)。究其原因,要追究 JavaScript 引擎对于 Math.random() 的实现原理,这里就不展开了(其实是我也不知道)。因为 ECMAScript 并没有规定 JavaScript 引擎对于 Math.random() 应该实现的方式,所以我猜想不同浏览器经过这样的乱序后,结果也不一样。

什么时候可以用这种方法乱序呢?"非正式" 场合,一些手写 DEMO 需要乱序的场合,这不失为一种 clever solution。

但是这种解法不但不正确,而且 sort 的复杂度,平均下来应该是 O(nlogn),跟我们接下来要说的正解还是有不少差距的。

Fisher–Yates Shuffle

关于数组乱序,正确的解法应该是 Fisher–Yates Shuffle,复杂度 O(n)。

其实它的思想非常的简单,遍历数组元素,将其与之前的任意元素交换。因为遍历有从前向后和从后往前两种方式,所以该算法大致也有两个版本的实现。

从后往前的版本:

function shuffle(array) {  var _array = array.concat();  for (var i = _array.length; i--; ) {    var j = Math.floor(Math.random() * (i + 1));    var temp = _array[i];
    _array[i] = _array[j];
    _array[j] = temp;
  }  
  return _array;
}

underscore 中采用从前往后遍历元素的方式,实现如下:

// Shuffle a collection, using the modern version of the// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle)._.shuffle = function(obj) {  var set = isArrayLike(obj) ? obj : _.values(obj);  var length = set.length;  var shuffled = Array(length);  for (var index = 0, rand; index < length; index++) {
    rand = _.random(0, index);    if (rand !== index) shuffled[index] = shuffled[rand];
    shuffled[rand] = set[index];
  }  return shuffled;
};

将其解耦分离出来,如下:

function shuffle(a) {  var length = a.length;  var shuffled = Array(length);  for (var index = 0, rand; index < length; index++) {
    rand = ~~(Math.random() * (index + 1));    if (rand !== index) 
      shuffled[index] = shuffled[rand];
    shuffled[rand] = a[index];
  }  return shuffled;
}

跟前面一样,做了下数据图表,猛戳 http://hanzichi.github.io/test-case/shuffle/Fisher-Yates/

关于证明,引用自月影老师的文章

随机性的数学归纳法证明

对 n 个数进行随机:

  1. 首先我们考虑 n = 2 的情况,根据算法,显然有 1/2 的概率两个数交换,有 1/2 的概率两个数不交换,因此对 n = 2 的情况,元素出现在每个位置的概率都是 1/2,满足随机性要求。

  2. 假设有 i 个数, i >= 2 时,算法随机性符合要求,即每个数出现在 i 个位置上每个位置的概率都是 1/i。

  3. 对于 i + 1 个数,按照我们的算法,在第一次循环时,每个数都有 1/(i+1) 的概率被交换到最末尾,所以每个元素出现在最末一位的概率都是 1/(i+1) 。而每个数也都有 i/(i+1) 的概率不被交换到最末尾,如果不被交换,从第二次循环开始还原成 i 个数随机,根据 2. 的假设,它们出现在 i 个位置的概率是 1/i。因此每个数出现在前 i 位任意一位的概率是 (i/(i+1)) * (1/i) = 1/(i+1),也是 1/(i+1)。

  4. 综合 1. 2. 3. 得出,对于任意 n >= 2,经过这个算法,每个元素出现在 n 个位置任意一个位置的概率都是 1/n。





返回列表 返回列表
评论

    分享到