You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于直方图/频率哈希表的随机抽样算法技术问询

Great question! Let's break this down into the two core problems you're tackling—weighted sampling with and without replacement from a frequency hash table—and walk through statistically valid approaches plus Common Lisp implementations.

Weighted Sampling from a Frequency Hash Table

1. Sampling with Replacement (Preserving Original Distribution)

To sample while retaining the original probability distribution (allowing duplicates), the standard approach is weighted random sampling using a cumulative distribution function (CDF). It’s straightforward to implement and works well for most practical use cases. Here’s how it works:

  • Calculate the total sum of all frequencies in your hash table. This represents the "range" of your weighted distribution.
  • Generate a random number between 0 (inclusive) and the total frequency sum (exclusive).
  • Iterate through the hash table, accumulating frequencies until the accumulated sum exceeds the random number. The corresponding key is your sampled item.

For high-performance repeated sampling, you could also use the Alias Method (precomputes a lookup table for O(1) sampling), but the CDF method is simpler for one-off or low-volume sampling tasks.

2. Sampling without Replacement: Is Your Approach Statistically Valid?

Short answer: Yes, your approach is fully compliant with statistical standards!

When you remove a sampled item from the hash table, you’re preserving the relative probabilities of the remaining items exactly as they should be for an unbiased无放回抽样. This is the standard way to perform weighted without-replacement sampling—each draw adjusts the available pool to maintain the correct relative weights for subsequent picks.

Common Lisp Implementation

Below are functions that handle both sampling modes, with options to modify the original hash table or work with a copy for non-destructive无放回抽样.

Helper & Core Functions

(defun total-frequency (hash-table)
  "Calculate the sum of all frequency values in the hash table."
  (let ((total 0))
    (maphash (lambda (k v) (declare (ignore k)) (incf total v)) hash-table)
    total))

(defun sample-with-replacement (hash-table)
  "Sample a single item from the hash table (with replacement, preserves original distribution)."
  (let* ((total (total-frequency hash-table))
         (r (random total)))
    (loop with accumulator = 0
          for key being the hash-keys of hash-table
          for freq = (gethash key hash-table)
          do (incf accumulator freq)
          when (> accumulator r)
            return key)))

(defun sample-without-replacement (hash-table &key (modify-original t))
  "Sample a single item without replacement. If MODIFY-ORIGINAL is T, alters the input hash table; otherwise returns sampled key + updated copy."
  (let* ((target-table (if modify-original
                           hash-table
                           (let ((copy (make-hash-table :test (hash-table-test hash-table))))
                             (maphash (lambda (k v) (setf (gethash k copy) v)) hash-table)
                             copy)))
         (total (total-frequency target-table))
         (sampled-key nil))
    (loop with accumulator = 0
          for key being the hash-keys of target-table
          for freq = (gethash key target-table)
          do (incf accumulator freq)
          when (> accumulator r)
            do (setf sampled-key key)
               (remhash key target-table)
               (return))
    (if modify-original
        sampled-key
        (values sampled-key target-table))))

;; Wrapper for easy mode selection
(defun weighted-sample (hash-table &key (replacement t) (modify-original t))
  "Sample an item from a frequency hash table. Use :REPLACEMENT NIL for no repeats; :MODIFY-ORIGINAL NIL to keep the input table intact."
  (if replacement
      (sample-with-replacement hash-table)
      (sample-without-replacement hash-table :modify-original modify-original)))

Example Usage

;; Create a sample frequency hash table
(defparameter *my-histogram* (make-hash-table :test 'equal))
(setf (gethash "apple" *my-histogram*) 3
      (gethash "banana" *my-histogram*) 5
      (gethash "cherry" *my-histogram*) 2)

;; With replacement sampling (can return duplicates)
(sample-with-replacement *my-histogram*) ; Might return "banana" most often

;; Without replacement (modifies original table)
(weighted-sample *my-histogram* :replacement nil) ; Removes the sampled item from *my-histogram*

;; Non-destructive without replacement
(multiple-value-bind (sampled remaining-table)
    (weighted-sample *my-histogram* :replacement nil :modify-original nil)
  (format t "Sampled: ~a~%Remaining items: ~a" sampled (alexandria:hash-table-alist remaining-table)))

Bulk Without-Replacement Sampling

If you need to draw multiple unique samples, use this helper:

(defun sample-n-without-replacement (hash-table n &key (modify-original t))
  "Draw N unique samples from the hash table. Returns a list of samples + remaining table (if non-destructive)."
  (let* ((target-table (if modify-original
                           hash-table
                           (let ((copy (make-hash-table :test (hash-table-test hash-table))))
                             (maphash (lambda (k v) (setf (gethash k copy) v)) hash-table)
                             copy)))
         (samples nil))
    (loop repeat n
          while (> (hash-table-count target-table) 0)
          do (push (sample-without-replacement target-table :modify-original t) samples))
    (if modify-original
        (nreverse samples)
        (values (nreverse samples) target-table))))

内容的提问来源于stack exchange,提问作者MadPhysicist

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 10:09:51