DifficultyDistributionService.php 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. <?php
  2. namespace App\Services;
  3. class DifficultyDistributionService
  4. {
  5. public function calculateDistribution(int $category, int $totalQuestions): array
  6. {
  7. switch ($category) {
  8. case 0:
  9. $mediumPercentage = 90; // 0-0.1
  10. $lowPercentage = 0;
  11. $highPercentage = 10; // 0.1-0.25
  12. break;
  13. case 1:
  14. $mediumPercentage = 90; // 0-0.25
  15. $lowPercentage = 0;
  16. $highPercentage = 10;
  17. break;
  18. case 2:
  19. $mediumPercentage = 50; // 0.25-0.5
  20. $lowPercentage = 25; // <0.25
  21. $highPercentage = 25; // >0.5
  22. break;
  23. case 3:
  24. $mediumPercentage = 80; // 0.5-0.75
  25. $lowPercentage = 10; // 0.4-0.5
  26. $highPercentage = 10; // >0.75
  27. break;
  28. case 4:
  29. $mediumPercentage = 50; // 0.75-0.9
  30. $lowPercentage = 25; // 0.5-0.75
  31. $highPercentage = 25; // 0.9-1.0
  32. break;
  33. default:
  34. $lowPercentage = 25;
  35. $mediumPercentage = 50;
  36. $highPercentage = 25;
  37. }
  38. $lowCount = (int) round($totalQuestions * $lowPercentage / 100);
  39. $mediumCount = (int) round($totalQuestions * $mediumPercentage / 100);
  40. $highCount = $totalQuestions - $lowCount - $mediumCount;
  41. return [
  42. 'low' => [
  43. 'percentage' => $lowPercentage,
  44. 'count' => $lowCount,
  45. 'label' => '低级难度'
  46. ],
  47. 'medium' => [
  48. 'percentage' => $mediumPercentage,
  49. 'count' => $mediumCount,
  50. 'label' => '基准难度'
  51. ],
  52. 'high' => [
  53. 'percentage' => $highPercentage,
  54. 'count' => $highCount,
  55. 'label' => '拔高难度'
  56. ]
  57. ];
  58. }
  59. public function getRanges(int $category): array
  60. {
  61. switch ($category) {
  62. case 0:
  63. return [
  64. 'primary' => ['min' => 0.0, 'max' => 0.1, 'percentage' => 90],
  65. 'secondary' => ['min' => 0.1, 'max' => 0.25, 'percentage' => 10],
  66. 'description' => '0基础型:0-0.1占比90%,0.1-0.25占比10%'
  67. ];
  68. case 1:
  69. return [
  70. 'primary' => ['min' => 0.0, 'max' => 0.25, 'percentage' => 90],
  71. 'secondary' => ['min' => 0.25, 'max' => 1.0, 'percentage' => 10],
  72. 'description' => '基础型:0-0.25占比90%,0.25-1占比10%'
  73. ];
  74. case 2:
  75. return [
  76. 'primary' => ['min' => 0.25, 'max' => 0.5, 'percentage' => 50],
  77. 'low' => ['min' => 0.0, 'max' => 0.25, 'percentage' => 25],
  78. 'high' => ['min' => 0.5, 'max' => 1.0, 'percentage' => 25],
  79. 'description' => '进阶型:0.25-0.5占比50%,<0.25占比25%,>0.5占比25%'
  80. ];
  81. case 3:
  82. return [
  83. 'primary' => ['min' => 0.5, 'max' => 0.75, 'percentage' => 80],
  84. 'low' => ['min' => 0.4, 'max' => 0.5, 'percentage' => 10],
  85. 'high' => ['min' => 0.75, 'max' => 1.0, 'percentage' => 10],
  86. 'fallback_low' => ['min' => 0.35, 'max' => 0.4, 'percentage' => 0],
  87. 'description' => '培优型:0.5-0.75占比80%,0.4-0.5占比10%,>0.75占比10%;0.35-0.4仅作为保题量兜底'
  88. ];
  89. case 4:
  90. return [
  91. 'primary' => ['min' => 0.75, 'max' => 0.9, 'percentage' => 50],
  92. 'low' => ['min' => 0.5, 'max' => 0.75, 'percentage' => 25],
  93. 'high' => ['min' => 0.9, 'max' => 1.0, 'percentage' => 25],
  94. 'secondary' => ['min' => 0.0, 'max' => 0.5, 'percentage' => 0],
  95. 'description' => '竞赛型:0.75-0.9占比50%,0.5-0.75占比25%,0.9-1.0占比25%'
  96. ];
  97. default:
  98. return [
  99. 'primary' => ['min' => 0.0, 'max' => 1.0, 'percentage' => 100],
  100. 'description' => '默认:全难度范围'
  101. ];
  102. }
  103. }
  104. public function groupQuestionsByDifficultyRange(array $questions, int $category): array
  105. {
  106. $buckets = [
  107. 'primary_low' => [],
  108. 'primary_medium' => [],
  109. 'primary_high' => [],
  110. 'secondary' => [],
  111. 'other' => []
  112. ];
  113. foreach ($questions as $question) {
  114. $difficulty = (float) ($question['difficulty'] ?? 0);
  115. $rangeKey = $this->classifyQuestionByDifficulty($difficulty, $category);
  116. $buckets[$rangeKey][] = $question;
  117. }
  118. return $buckets;
  119. }
  120. public function classifyQuestionByDifficulty(float $difficulty, int $category): string
  121. {
  122. switch ($category) {
  123. case 0:
  124. if ($difficulty >= 0 && $difficulty <= 0.1) {
  125. return 'primary_medium';
  126. }
  127. if ($difficulty > 0.1 && $difficulty <= 0.25) {
  128. return 'secondary';
  129. }
  130. return 'other';
  131. case 1:
  132. if ($difficulty >= 0 && $difficulty <= 0.25) {
  133. return 'primary_medium';
  134. }
  135. return 'secondary';
  136. case 2:
  137. if ($difficulty >= 0.25 && $difficulty <= 0.5) {
  138. return 'primary_medium';
  139. }
  140. if ($difficulty < 0.25) {
  141. return 'primary_low';
  142. }
  143. return 'primary_high';
  144. case 3:
  145. if ($difficulty >= 0.5 && $difficulty <= 0.75) {
  146. return 'primary_medium';
  147. }
  148. if ($difficulty >= 0.4 && $difficulty < 0.5) {
  149. return 'primary_low';
  150. }
  151. if ($difficulty >= 0.35 && $difficulty < 0.4) {
  152. return 'secondary';
  153. }
  154. if ($difficulty < 0.35) {
  155. return 'other';
  156. }
  157. return 'primary_high';
  158. case 4:
  159. if ($difficulty >= 0.75 && $difficulty < 0.9) {
  160. return 'primary_medium';
  161. }
  162. if ($difficulty >= 0.5 && $difficulty < 0.75) {
  163. return 'primary_low';
  164. }
  165. if ($difficulty >= 0.9 && $difficulty <= 1.0) {
  166. return 'primary_high';
  167. }
  168. return 'secondary';
  169. default:
  170. return 'other';
  171. }
  172. }
  173. public function mapDifficultyLevelToRangeKey(string $level, int $category): string
  174. {
  175. switch ($category) {
  176. case 0:
  177. return match($level) {
  178. 'low' => 'secondary',
  179. 'medium' => 'primary_medium',
  180. 'high' => 'secondary',
  181. default => 'secondary'
  182. };
  183. case 1:
  184. return match($level) {
  185. 'low' => 'secondary',
  186. 'medium' => 'primary_medium',
  187. 'high' => 'secondary',
  188. default => 'secondary'
  189. };
  190. case 2:
  191. return match($level) {
  192. 'low' => 'primary_low',
  193. 'medium' => 'primary_medium',
  194. 'high' => 'primary_high',
  195. default => 'other'
  196. };
  197. case 3:
  198. return match($level) {
  199. 'low' => 'primary_low',
  200. 'medium' => 'primary_medium',
  201. 'high' => 'primary_high',
  202. default => 'other'
  203. };
  204. case 4:
  205. return match($level) {
  206. 'low' => 'primary_low',
  207. 'medium' => 'primary_medium',
  208. 'high' => 'primary_high',
  209. default => 'other'
  210. };
  211. default:
  212. return 'other';
  213. }
  214. }
  215. public function getSupplementOrder(int $category): array
  216. {
  217. return match ($category) {
  218. 0, 1 => ['secondary', 'other'],
  219. 2 => ['primary_medium', 'primary_low', 'primary_high', 'other'],
  220. 3 => ['primary_medium', 'primary_high', 'primary_low', 'secondary', 'other'],
  221. 4 => ['primary_high', 'primary_medium', 'primary_low', 'secondary', 'other'],
  222. default => ['other']
  223. };
  224. }
  225. }