PHP切割整数工具类似微信红包金额分配的思路详解

2025-05-27 0 68

Composer地址:https://packagist.org/packages/werbenhu/php-number-slicing

GitHub地址:https://github.com/werbenhu/php-number-slicing

主要代码:NumberSlicing.php

思路:将数字按精度放大倍数,比如切割数字1,切割的份数是10,精度是0.01,则将1放大100 X 10倍,然后再来对加了1000倍权重后的值进行切割。切割完成之后,再将权重去除,保证总值是1。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148
<?php

namespace Werben\\Tools;

use Exception;

class NumberSlicing {

/**

* 精确小数点,舍弃最后一位之后的数据(非四舍五入)

* floor with precision

* @param $number 要精确的数

* @param $precision 精度,比如保留到0.01,则该值为2

* @return float|int

*/

public static function floorWithPrecision($number, $precision) {

$power = pow(10, $precision);

$ret = floor($number * $power) * 1.0 / $power ;

return $ret;

}

/**

* 精确小数点,按四舍五入保留最后一位

* round with precision

* @param $number 要精确的数

* @param $precision 精度,比如保留到0.01,则该值为2

* @return float|int

*/

public static function roundWithPrecision($number, $precision) {

$power = pow(10, $precision);

$ret = round($number * $power) * 1.0 / $power ;

return $ret;

}

/**

* 将数把权重放大,比如1,要按精度0.0001分配,则先将1乘以10000然后再来分配

* random the sum weights 加上权重之后,整个要切割的数的权重总值

* @param $weight_items 用来保留,随机分配的权重值

* @param $count 要切割的份数

* @param int $each_weight 加上权重之后,每一份平均的权重值

* @param int $min_weight 加上权重之后,最小额度的值

* @return float|int

*/

public static function weightSlicing(&$weight_items, $count, $each_weight = 10, $min_weight = 3)

{

$already_count = count($weight_items);

$cur_random_full_total = ($already_count + 1) * $each_weight;

$already_random_real_total = 0;

foreach ($weight_items as $value) {

$already_random_real_total += $value;

}

$cur_random_rest = $cur_random_full_total - $already_random_real_total;

if ($already_count == $count - 1) {

$cur_random_rate = $cur_random_rest;

} else {

$cur_random_rate_max = $cur_random_rest + $each_weight - $min_weight * 2;

$cur_random_rate = $min_weight + mt_rand(0, $cur_random_rate_max);

}

$weight_items[] = $cur_random_rate;

return $cur_random_rate;

}

/**

* slicing the number

* @param int $number

* @param int $size

* @param float $precision

* @param float $min

* @return array

* @throws Exception

*/

public static function numberSlicing($number, $size, $precision = 0.01, $min = 0.01) {

if ($number * 1.0 / $size <= $min) {

throw new Exception('min number is bigger than the average value!');

}

if ($precision > 1) {

throw new Exception('precision can\\'t bigger than 1!');

}

if ($min < $precision) {

throw new Exception('precision can\\'t bigger than min!');

}

$weight_items = [];

$items = [];

//不加权重情况下,每一份的平均值

$each_weight = intval($number / $size);

if ($precision < 1) {

//如果精度是小数

if ($each_weight > 1) {

//如果平均值大于1,则最小额度则直接用min就可以了

//每一份的平均值乘以权重的值,比如精度为0.01,则每一份的平均值要乘以权重(100)

$each_weight = intval((1 / $precision) * $number / $size);

//最小数值也要乘以权重

$min_weight = intval(1 / $precision) * $min;

} else {

//如果平均值小于1,需要将平均值也乘以权重

$each_weight = intval(1 / $precision);

$min_weight = $each_weight * $size * $min / $number;

}

$precision_num = log10(1 / $precision);

} else {

//如果精度是整数(1)

$min_weight = $min;

$precision_num = 0;

}

$sum_item_number = 0.0;

$sum_weight = 0.0;

//先将整个数,随机按最小额度分配

for ($i = 0; $i < $size; $i++) {

$cur_weight = self::weightSlicing($weight_items, $size, $each_weight, $min_weight);

//将权重去除,换算回原先的比例

$rate = ($number * $cur_weight * 1.00) / ($size * $each_weight);

$rate = self::floorWithPrecision($rate, $precision_num);

$sum_item_number += $rate;

$sum_weight += $cur_weight;

$items[] = $rate;

}

//由于误差,随机分配后,还会遗留一些数没有完全分配完,则将剩下的数随机分配

if ($precision_num != 0) {

//如果是切割成小数

$rest = $number - $sum_item_number;

while ($rest - 0.00 > PHP_FLOAT_MIN) {

if ($rest / $min >= 1.0) {

//剩余的数大于min最小额度,则将每份最小额度随机分配

$random_index = mt_rand(0, $size - 1);

$items[$random_index] = self::roundWithPrecision($items[$random_index] + $min, $precision_num);

$sum_item_number = self::roundWithPrecision($sum_item_number + $min, $precision_num);

$rest = self::roundWithPrecision($number - $sum_item_number, $precision_num);

} else {

//剩余的数小于min最小额度,则将这最后的未分配的数随机分配

$random_index = mt_rand(0, $size - 1);

$items[$random_index] = self::roundWithPrecision($items[$random_index] + $number - $sum_item_number, $precision_num);

$sum_item_number = $number;

$rest = $number - $sum_item_number;

}

}

} else {

//如果是切割成整数

$rest = $number - $sum_item_number;

while ($rest > 0) {

if ($rest / $min >= 1) {

$random_index = mt_rand(0, $size - 1);

$items[$random_index] += $min;

$sum_item_number += $min;

$rest = $number - $sum_item_number;

} else {

$random_index = mt_rand(0, $size - 1);

$items[$random_index] += $rest;

$sum_item_number += $rest;

$rest = $number - $sum_item_number;

}

}

}

return $items;

}

}

  测试代码:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74
use Werben\\Tools\\NumberSlicing;

function testIntSlicing2IntOne() {

$precision = 1; //精确度 eg: 1, 0.1, 0.01, 0.01

$size = 10; //切割的份数,the size of the number to slicing

$min = 3; //最小额度,最小额度必须大于最小精度,min amount eg: 3, 0.23, 0.05, 0.008

$number = 100; //要切割的数字,the number

$items = NumberSlicing::numberSlicing($number, $size, $precision, $min);

$sum = 0.0;

$ret_min = $number;

foreach ($items as $value) {

$sum += $value;

if ($ret_min > $value) {

$ret_min = $value;

}

}

$count = count($items);

echo "count: $count, sum: $sum, ret_min: $ret_min\\n";

echo "items : ". json_encode($items) ."\\n";

}

function testIntSlicing2IntTwo() {

$precision = 1; //精确度 eg: 1, 0.1, 0.01, 0.01

$size = 30; //切割的份数,the size of the number to slicing

$min = 18666; //最小额度,最小额度必须大于最小精度,min amount eg: 3, 0.23, 0.05, 0.008

$number = 800000; //要切割的数字,the number

$items = NumberSlicing::numberSlicing($number, $size, $precision, $min);

$sum = 0.0;

$ret_min = $number;

foreach ($items as $value) {

$sum += $value;

if ($ret_min > $value) {

$ret_min = $value;

}

}

$count = count($items);

echo "count: $count, sum: $sum, ret_min: $ret_min\\n";

echo "items : ". json_encode($items) ."\\n";

}

function testIntSlicing2FloatOne() {

$precision = 0.01; //精确度 eg: 1, 0.1, 0.01, 0.01

$size = 1000; //切割的份数,the size of the number to slicing

$min = 0.05; //最小额度,最小额度必须大于最小精度,min amount eg: 3, 0.23, 0.05, 0.008

$number = 100; //要切割的数字,the number

$items = NumberSlicing::numberSlicing($number, $size, $precision, $min);

$sum = 0.0;

$ret_min = $number;

foreach ($items as $key => $value) {

$sum += $value;

if ($ret_min > $value) {

$ret_min = $value;

}

}

$count = count($items);

echo "count: $count, sum: $sum, ret_min: $ret_min\\n";

echo "items: ". json_encode($items) ."\\n";

}

function testIntSlicing2FloatTwo() {

$precision = 0.00001; //精确度 eg: 1, 0.1, 0.01, 0.01

$size = 1000; //切割的份数,the size of the number to slicing

$min = 0.00005; //最小额度,最小额度必须大于最小精度,min amount eg: 3, 0.23, 0.05, 0.008

$number = 5; //要切割的数字,the number

$items = NumberSlicing::numberSlicing($number, $size, $precision, $min);

$sum = 0.0;

$ret_min = $number;

foreach ($items as $key => $value) {

$sum += $value;

if ($ret_min > $value) {

$ret_min = $value;

}

}

$count = count($items);

echo "count: $count, sum: $sum, ret_min: $ret_min\\n";

echo "items: ". json_encode($items) ."\\n";

}

总结

以上所述是小编给大家介绍的PHP切割整数工具类似微信红包金额分配的思路详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对快网idc网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

原文链接:https://www.cnblogs.com/werben/archive/2019/09/18/11540133.html

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 PHP切割整数工具类似微信红包金额分配的思路详解 https://www.kuaiidc.com/71526.html

相关文章

发表评论
暂无评论