PHP抽奖概率算法


/*
* 不同概率的抽奖原理就是把0到*(比重总数)的区间分块
* 分块的依据是物品占整个的比重,再根据随机数种子来产生0-* 中的某个数
* 判断这个数是落在哪个区间上,区间对应的就是抽到的那个物品。
* 随机数理论上是概率均等的,那么相应的区间所含数的多少就体现了抽奖物品概率的不同。
*/
function get_rand($proArr)
{
$result = array();
foreach ($proArr as $key => $val) {
$arr[$key] = $val['v'];
}
$proSum = array_sum($arr); // 计算总权重
$randNum = mt_rand(1, $proSum);
$d1 = 0;
$d2 = 0;
for ($i=0; $i < count($arr); $i++) { $d2 += $arr[$i]; if($i==0) { $d1 = 0; } else { $d1 += $arr[$i-1]; } if($randNum >= $d1 && $randNum <= $d2) { $result = $proArr[$i]; } } unset ($arr); return $result; } /* * 使用较多的为这个方法 */ function get_rand1($proArr) { $result = array(); foreach ($proArr as $key => $val) {
$arr[$key] = $val['v'];
}
// 概率数组的总概率
$proSum = array_sum($arr);
asort($arr);
// 概率数组循环
foreach ($arr as $k => $v) {
$randNum = mt_rand(1, $proSum);
if ($randNum <= $v) { $result = $proArr[$k]; break; } else { $proSum -= $v; } } return $result; } /* * 奖项数组 * 奖品id,名称,比重 */ $arr = array( array('id'=>1,'name'=>'特等奖','v'=>1),
array('id'=>2,'name'=>'一等奖','v'=>5),
array('id'=>3,'name'=>'二等奖','v'=>10),
array('id'=>4,'name'=>'三等奖','v'=>12),
array('id'=>5,'name'=>'四等奖','v'=>22),
array('id'=>6,'name'=>'没中奖','v'=>50)
);

测试结果(10000次):
get_rand():
count_1:0 count_2:490 count_3:1021 count_4:1172 count_5:2172 count_6:5145
特等奖中奖率全为:0
get_rand1():
count_1:92 count_2:477 count_3:1017 count_4:1195 count_5:2264 count_6:4955
总体感觉 get_rand1() 更准确些……

5分钟快速实现一个哈希表(Clang)

哈希表(Hash table,也叫散列表),是根据键(Key)而直接访问在内存存储位置的数据结构。 也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。 这个映射函数称做散列函数,存放记录的数组称做散列表。 ————维基百科

在一些合理的假设下,在哈希表中的所有操作的时间复杂度可以简单看作O(1) 。它通过计算一个键的哈希值来快速定位键值在哈希表的位置。 实现一个好的哈希表的关键是一个好的哈希算法与一个处理哈希冲突的方法 。常见的简单哈希算法有BKDRHash,APHash,DJBHash,JSHash,RSHash.较复杂有MD5和SHA1之流。哈希算法的好坏直接影响哈希表存取的效率。 而处理哈希冲突的办法有开放寻址法与拉链法。下面示例使用的是拉链法

BKDRHash哈希算法

上述多个哈希算法中 BKDRHash在效率,平衡,均匀分布方面表现突出,并且它的实现也是最容易理解与记忆的。实现如下:

/**  * BKDR hash function in clang * @param key char* 字符串 * @return int hashed value, we will get a int value */int bkdrHash( char *key ){    uint seed = 131;//i can be 13, 131, 1313,131313 etc    uint hash = 0;    while( *key != ‘\n’ && *key != 0 )    {            hash = hash * seed + ( *key++ );    }    return ( hash & 0x7FFFFFFF );}

哈希表(拉链法处理冲突)

示意图:(图片来源:http://blog.csdn.net/v_JULY_v/article/details/6256463)

上图中,左则是一个数组,保存的是每个单向链表的头指针。实际的值就存储在链表中

单向链表中的节点的数据结构与数组定义代码

/** 单向链表的节点的结构体 */typedef struct _htItem{        struct _htItem  *next;        char *key_string;        uint fid;} htItem;/** 取得Key在数组中的映射位置 */uint htIndex( char *key, htItem **ht ){        uint hashedKey = bkdrHash( key );        uint length = (ht[0]->fid-1);        return (uint)hashedKey % length+1;}/** 定义数组 */htItem *item[21]; //为方便后台实现,在定义在数组的时候,我们给多一个长度,第一个数据不存储数据,只存放数组长度,剩余的元素空间

初始化数组元素

/** 有必要对数组元素进行一次0 初始化 */void htInit( htItem **ht, uint length ){         int i;         for( i=0; i<length; i++ ){                 ht[i] = (htItem*)malloc(sizeof(htItem));                  memset( ht[i], 0, sizeof(htItem) );         }         ht[0]->fid = length;}

数据的插入和更新

首先通过哈希函数找到Key对应在数组中的位置,再遍历链表,如果在链表中查到相同的key,就直接把该节点的数据更新,否则malloC一个节点,并把节点放到链表末尾。

/** set hashTable element */uint htSet( char *key, uint fid,  htItem **ht ){         uint i = htIndex( key, ht );         htItem *item = ht[i];         while( item->next )         {                  if( strcmp(key, item->next->key_string) == 0 ){                          item->next->fid = fid;                          return 0;                 }else{                          item = item->next;                 }         }         item->next = (htItem*)malloc(sizeof(htItem));         item->next->fid = fid;         item->next->key_string = key;         return 0;}

获取数据

首先通过哈希函数定位KEY在数组中的位置,再依次对比KEY值,如果命中就返回相应的值,否则返回空值

/** get hashTable elements */htItem* htGet( char *key, htItem **ht ){        uint i = htIndex( key, ht);        htItem *item = ht[i]->next;         htItem *tmp = (htItem*)malloc(sizeof(htItem));         memset(tmp, 0, sizeof(htItem));        while( item )        {                if( strcmp(key, item->key_string) == 0 ){                          printf(“hiting %s\n”, key);                          tmp->fid = item->fid;                          tmp->key_string = item->key_string;                        return tmp;                }                 item = item->next;        }        return;}

删除数据

/** delete one element of hashtable */int htDel(char *key, htItem **ht){        uint i = htIndex(key, ht);        htItem *item = ht[i];        while(item->next){                if(strcmp(key, item->next->key_string) == 0 ){                        htItem *tmp = item->next;                        item->next = tmp->next;                        free(tmp);                        return 0;                }                item = item->next;        }        return -1;}

打印完整hashtable中的元素

void print_hashTable( htItem **ht ){        uint length = ht[0]->fid;        uint i;        htItem *item;        for( i = 1; i < length; i++ )        {                item = ht[i]->next;                while(item)                {                        printf(“%s => %d\n”,item->key_string, item->fid);                        item = item->next;                }        }}

简单几个方法就实现了哈希表的增删查改。

使用示例

/** 定义与初始化 */    htItem *item[101];          htInit(item, 101);                  /** 添加元素 */         htSet(“longmon”,100,item);          htSet(“natalie”, 1000,item);         htSet(“xiaoqiong”,99, item);                  /** 打印 */         print_hashTable(item);                  /** 删除一个元素 */         htDel(“natalie”, item);                  /** 打印 */         print_hashTable(item);                  /** 获取keylongmon的值 */         htItem *tmpitem = htGet(“longmon”, item);                   /** 打印 */         printf(“key: %s => val:%d\n”, tmpitem->key_string, tmpitem->fid);

输出结果

#打印 xiaoqiong => 99natalie => 1000longmon => 100#打印 xiaoqiong => 99longmon => 100#打印 Key: longmon => val: 100

完整代码 https://github.com/longmon/hashTable_in_c

哈希表是PHP内核的基石。

PHP内核中的哈希表是十分重要的数据结构,PHP的大部分的语言特性都是基于哈希表实现的, 例如:变量的作用域、函数表、类的属性、方法等,Zend引擎内部的很多数据都是保存在哈希表中的。而PHP内核中的哈希表处理冲突使用的也是拉链法。

扩展阅读:

算法效率的度量方法

一、事后统计法

事后统计法:这种方法主要是通过设计好的测试程序和数据,利用计算机计时器对不同算法编制的程序的运行时间进行比较,从而确定算法效率的高低。

缺点:

必须依据算法事先编制好程序,这通常需要花费大量时间和精力。

时间的比较依赖计算机硬件和软件等环境因素,有时会掩盖算法本身的优劣。

算法的测试数据设计困难,并且程序的运行时间往往还与测试数据的规模有很大关系,效率高的算法在小的测试数据面前往往得不到体现。

二、事前分析估算方法

事前分析估算方法:在计算机程序编制前,依据统计方法对算法进行估算。

一个用高级程序语言编写的程序在计算机上运行时所消耗的时间取决于下列因素:

1.算法采用的策略、方法。(算法好坏的根本)

2.编译产生的代码质量。(软件支持)

3.问题的输入规模。

4.机器执行指令的速度。(硬件性能)

抛开计算机硬件、软件有关的因素,一个程序的运行时间,依赖于算法的好坏和问题的输入规模。所谓问题输入规模是指输入量的多少。

测定运行时间最可靠的方法就是计算对运行时间有消耗的基本操作的执行次数。运行时间与这个计数成正比。

算法设计的要求

一、正确性

正确性:算法的正确性是指算法至少应该具有输入、输出和加工处理无歧义性、能正确反映问题的需求、能够得到问题的正确答案。

算法“正确”的层次:

1.算法程序没有语法错误

2.算法程序对于合法的输入数据能够产生满足要求的输出结果。

3.算法程序对于非法的输入数据能够得出满足规格说明的结果。(一般以此为正确的标准)

4.算法程序对于精心选择的,甚至刁难的测试数据都有满足要求的输出结果。

二、可读性

可读性:算法设计的另一目的是为了便于阅读、理解和交流。(算法好坏的重要标志)

三、健壮性

健壮性:当输入数据不合法时,算法也能做出相关处理,而不是产生异常或莫名其妙的结果。

四、时间效率高和存储量低

 

算法

一、冒泡排序:

1、算法思想:

对要排序的数据,从上到下依次比较两个相邻的数并加以调整,将最大的数向下移动,较小的数向上冒起。即:每一趟依次比较相邻的两个数据元素,将较小的数放在左边,循环进行同样的操作,直到全部待排序的数据元素排完。

2、实例分析:

例如:我们要将身高不等的十个人站在一排,要求他们按照身高由低到高排队,设将10个人编号为0—9 ,相邻的两个人依次比较,如果左边的比右边的人高,则交换两个人的位置,否则不交换,直到最后两个人,即此时完成了一趟排序。一趟排序后,最高的人已经在最右边了。

 

一趟排序后的结果:(将最高者一趟排序后移动到最后位置)

如此都多趟的排序,最终就完成了整个的排序。

3、算法分析:(有小到大排序)

1>、每一趟过程中,就是依次比较两个相邻的数,若a[i]>a[i+1],则交换两数,否则不换;

2>、每一趟就是一重循环,而由于要经过多趟过程,即外面还有一重循环,所以就存在双重循环。

4、算法代码:(Java版)

  1. /**
  2.  * 冒泡排序 算法
  3.  * @author xcbeyond
  4.  *
  5.  */
  6. public class BubbleSort {
  7.     public static void main(String[] args) {
  8.         // TODO Auto-generated method stub
  9.         int a[] ={13,15,37,89,60,39,12,109,56,72} ;
  10.         int i = 0;
  11.         int j = 0;
  12.         for(i=0;i<10;i++)
  13.             System.out.print(a[i]+”  “);
  14.         System.out.println();
  15.         for(i=0;i<a.length;i++)
  16.             for(j=0;j<a.length-i-1;j++)
  17.             {
  18.                 if(a[j]>a[j+1])
  19.                 {
  20.                     int temp;
  21.                     temp=a[j];
  22.                     a[j]=a[j+1];
  23.                     a[j+1]=temp;
  24.                 }
  25.             }
  26.         for(i=0;i<10;i++)
  27.             System.out.print(a[i]+”  “);
  28.     }
  29. }

二、选择排序

1、算法思想:

将待排序序列分为两部分,一部分为有序序列,另一部分为无序序列。第一趟:从a[0]到a[n-1]中找到最小的数a[i],然后将a[i]与a[0]交换,第二趟:从a[1]到a[n-1]中找到最小的数a[j],然后将a[j]与a[1]交换,第三趟:从a[2]到a[n-1]中找到最小的数a[k],然后将a[k]与a[2]交换 ……

2、实例分析:

{13,15,37,89,60,39,12,109,56,72}

第一趟 :12  {15,37,89,60,39,13,109,56,72}

第二趟:12 ,13 {37,89,60,39,15,109,56,72}

第三趟:12 ,13 ,15 {89,60,39,37,109,56,72}

……

3、算法代码:

 

  1. /**
  2.  * 选择排序
  3.  * @author xcbeyond
  4.  *
  5.  */
  6. public class SelectSort {
  7.     public static void main(String[] args) {
  8.         // TODO Auto-generated method stub
  9.         int a[] ={13,15,37,89,60,39,12,109,56,72} ;
  10.         int i;
  11.         int j;
  12.         for(i = 0;i<a.length;i++)
  13.             System.out.print(a[i]+”  “);
  14.         System.out.println();
  15.         for(i = 0;i<a.length;i++){
  16.             int min = a[i];
  17.             for(j = i+1;j<a.length;j++){
  18.                 if(min>a[j]){
  19.                     int temp;
  20.                     temp = min;
  21.                     min = a[j];
  22.                     a[j] = temp;
  23.                 }
  24.             }
  25.             a[i] = min;
  26.         }
  27.         for(i = 0;i<a.length;i++)
  28.             System.out.print(a[i]+”  “);
  29.     }
  30. }

三、插入排序

1、算法思想:

1〉从第一个元素开始,该元素可以认为已经被排序

2〉取出第一个未排序元素存放在临时变量temp中,在已经排序的元素序列中从后往前扫描,逐一比较

3〉如果temp小于已排序元素,将该元素移到下个位置

4〉重复步骤3〉,直到找到已排序的元素小于或者等于

2、实例分析:

{13,15,37,89,60,39,12,109,56,72}

第一趟:   13 {15,37,89,60,39,12,109,56,72}     temp = 15

temp>13

第二趟:   13 ,15 { 37,89,60,39,12,109,56,72}      temp = 37

temp>15         temp>13
第三趟: 13,15 ,37  {89,60,39,12,109,56,72}     temp = 89

temp>37       temp>15       temp>13

第三趟: 13,15 ,37 ,89 {60,39,12,109,56,72}     temp =60

temp< 89:60<->89          13,15 ,37 , 60,89 {39,12,109,56,72}

temp>37        temp>15        temp>13

第四趟: 13,15 ,37 , 60,89  {39,12,109,56,72}        temp = 39

temp<89:  39<->89           13,15 ,37 , 60,39,89 {12,109,56,72}

temp<60  :   39<->60           13,15 ,37 , 39,60,89 {12,109,56,72}

temp>37      temp>15         temp >13

第五趟:    13,15 ,37 , 39,60,89 {12,109,56,72}       temp = 12

temp<89 :12<->89                   13,15 ,37 ,39,60, 12,89 {109,56,72}

temp<60:12<->60                    13,15 ,37 ,39,12,60,89 {109,56,72}

temp<39 :12<->39                   13,15 ,37 ,12,39,60,89 {109,56,72}

temp<37 :12<->37                    13,15 ,12 ,37 ,39,60,89 {109,56,72}

temp<15: 12<->15                   13,12,15 ,37 ,39,60,89 {109,56,72}

temp<13 :12<->13                     12,13,15 ,37 ,39,60,89 {109,56,72}

第六趟:  12,13,15 ,37 ,39,60,89 {109,56,72}           temp = 109

……

 

3、算法代码:

  1. import java.util.Arrays;
  2. /**
  3.  * 插入排序
  4.  * @author xcbeyond
  5.  *
  6.  */
  7. public class InsertSort {
  8.     public static void main(String[] args) {
  9.         // TODO Auto-generated method stub
  10.         int a[] ={13,15,37,89,60,39,12,109,56,72} ;
  11.         a = insertSort(a);
  12.         String s = Arrays.toString(a);
  13.         System.out.println(s);
  14.     }
  15.     public static int[] insertSort(int[] ary){
  16.         for(int i = 1;i < ary.length;i++){
  17.             int temp = ary[i];
  18.             int j;
  19.             for(j = i-1;j>=0 && temp < ary[j];j–){
  20.                 ary[j+1] = ary[j];
  21.             }
  22.             ary[j+1] = temp;
  23.         }
  24.         return ary;
  25.     }
  26. }

 

  1. import java.util.Arrays;
  2. /**
  3.  * 插入排序
  4.  * @author xcbeyond
  5.  *
  6.  */
  7. public class InsertSort {
  8.     public static void main(String[] args) {
  9.         // TODO Auto-generated method stub
  10.         int a[] ={13,15,37,89,60,39,12,109,56,72} ;
  11.         a = insertSort(a);
  12.         String s = Arrays.toString(a);
  13.         System.out.println(s);
  14.     }
  15.     public static int[] insertSort(int[] ary){
  16.         for(int i = 1;i < ary.length;i++){
  17.             int temp = ary[i];
  18.             int j;
  19.             for(j = i-1;j>=0;j–){
  20.                 if(temp < ary[j]){
  21.                     ary[j+1] = ary[j];
  22.                 }
  23.                 else
  24.                     break//找到插入位置
  25.             }
  26.             ary[j+1] = temp;
  27.         }
  28.         return ary;
  29.     }
  30. }