Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/290.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 气泡洗牌-加权洗牌_Python_List_Algorithm_Shuffle_Bubble Sort - Fatal编程技术网

Python 气泡洗牌-加权洗牌

Python 气泡洗牌-加权洗牌,python,list,algorithm,shuffle,bubble-sort,Python,List,Algorithm,Shuffle,Bubble Sort,可以设想对冒泡排序进行修改,其中“交换”以概率p随机发生,而不是通过执行比较。结果可以称为“泡沫洗牌”。靠近最前面的元素可能会留在那里,但有可能被转移到列表的后面 修改从互联网上窃取的冒泡排序,您可能会得出以下结论: 随机导入 def气泡_混洗(arr,p): arr=复制。复制(arr) n=长度(arr) #遍历所有数组元素 对于范围(n-1)内的i: #范围(n)也可以工作,但外循环将重复一次以上。 #最后一个i元素已经就位 对于范围(0,n-i-1)内的j: #从0遍历数组到n-i-1

可以设想对冒泡排序进行修改,其中“交换”以概率
p
随机发生,而不是通过执行比较。结果可以称为“泡沫洗牌”。靠近最前面的元素可能会留在那里,但有可能被转移到列表的后面

修改从互联网上窃取的冒泡排序,您可能会得出以下结论:

随机导入
def气泡_混洗(arr,p):
arr=复制。复制(arr)
n=长度(arr)
#遍历所有数组元素
对于范围(n-1)内的i:
#范围(n)也可以工作,但外循环将重复一次以上。
#最后一个i元素已经就位
对于范围(0,n-i-1)内的j:
#从0遍历数组到n-i-1
#如果随机数[0,1]小于p,则交换
如果是random.random()
该算法是n平方阶的。。。但是一个元素在任何特定地点结束的概率应该是可以提前计算的,所以它不需要n平方。 是否可以采取更有效的方法


我曾考虑过从几何分布中取样,并将其添加到原始索引中(加上
(len(n)-I-1)/len(n)
,以打破关系),但这并不能提供相同的动态。

对于每(n,p)只需进行一次预计算的情况,我们可以在预期的线性时间内模拟bubble_shuffle运行(不包括预计算)

方法: 获取bub(n,p):O(n^2)方法来运行bubble\u shuffle

get_expected_bub(n,p):O(n^2)方法来计算运行bubble\u shuffle的每个位置的预期平均值

get_dist(pos,p):simbub使用的O(1)方法,该方法根据使用的p获取随机数的连续交换

get_simbub(n,p_arr):模拟运行气泡洗牌的预期O(n*min(n,(1/(1-p)))方法。对于p=0.5,这是预期的O(n)。对于p=1-(1/n),这是O(n^2)

get_expected_simbub(n,p_arr):O(n^2)方法,用于计算运行simbub的每个位置的预期平均值

get_p_arr(n,p,公差):为给定的n&p查找将simbub与bubble_shuffle(公差范围内)对齐的p_arr的方法

比较(n,p,p_arr,trials):多次运行simbub并将结果与bubble_shuffle的预期结果进行比较的方法

time_trials(n,p,seconds):对于给定的n&p,在输入秒数内运行bubble_shuffle和simbub,并比较我们能够完成的运行次数

所有代码都是Ruby的

# Run bubble_shuffle
def get_bub(n, p)
  arr = [*0..(n-1)]
  0.upto(n-1) do |i|
    0.upto(n-i-2) do |j|
      if rand < p
        arr[j], arr[j+1] = arr[j+1], arr[j]
      end
    end
  end
  return arr
end


# Get the expected average results of running bubble_shuffle many times
# This works by iteratively distributing value according to p.
def get_expected_bub(n, p)
  arr = [*0.upto(n-1)]  
  
  (n-1).downto(0) do |last_index|
    working_arr = arr.clone
    0.upto(last_index) do |i|
      working_arr[i] = 0
    end
    0.upto(last_index) do |source_index|

      min_sink = [0, source_index-1].max
      max_sink = last_index
      min_sink.upto(max_sink) do |sink_index|
        portion = 1.0
        if sink_index == source_index - 1
          portion *= p
        else
          portion *= (1-p) if source_index > 0
          portion *= (p**(sink_index - source_index)) if sink_index > source_index
          portion *= (1-p) if sink_index < last_index
        end
        working_arr[sink_index] += arr[source_index] * portion
      end

    end
    0.upto(last_index) do |i|
      arr[i] = working_arr[i]
    end
  end
  return arr
end


# For simbub, randomly get the distance to the index being swapped into 
# the current position
def get_dist(pos, p)
  return 0 if pos == 0
  return [pos, Math.log(1 - rand, p).floor].min
end


# Run simbub from the last-to-first index
# p_arr is the array of probabilities corresponding to the effective probability
# of swapping used at each position. The last value of this array will always
# equal the p value being simulated. So will the first, though this is not used.
def get_simbub(n, p_arr)
  arr = [*0..(n-1)]
  (n-1).downto(0) do |pos|
    p = p_arr[pos]
    dist = get_dist(pos, p)
    if dist > 0
      val_moving_up = arr[pos - dist]
      (pos - dist).upto(pos - 1) do |j|
        arr[j] = arr[j+1]
      end
      arr[pos] = val_moving_up
    end
  end
  return arr
end


# Get the expected average results of running simbub many times
# This works by iteratively distributing value according to p_arr.
def get_expected_simbub(n, p_arr)
  arr = [*0.upto(n-1)]  
  
  (n-1).downto(1) do |last_index|
    working_arr = arr.clone
    0.upto(last_index) do |i|
      working_arr[i] = 0
    end
    
    p = p_arr[last_index]
    cum_p_distance = 0
    0.upto(last_index) do |distance|

      if distance == last_index
        p_distance = p ** distance
      else
        p_distance = (1-p) * (p ** distance)
      end
      
      working_arr[last_index] += p_distance * arr[last_index - distance]
      
      if distance >= 1
        working_arr[last_index - distance] = arr[last_index - distance] + (1 - cum_p_distance) * (arr[last_index - distance + 1] - arr[last_index - distance])
      end
     
      cum_p_distance += p_distance
    end
    arr = working_arr
  end
  return arr
end


# Solve for the p_arr that yields the same expected averages for simbub for 
# each position (within tolerance) as bub
def get_p_arr(n, p, tolerance = 0.00001)
  expected_bub = get_expected_bub(n, p)
  p_arr = [p] * n
  
  (n-2).downto(1) do |pos|
    min_pos_p = 0.0
    max_pos_p = 1.0
    while true do
      expected_simbub = get_expected_simbub(n, p_arr)
      if expected_simbub[pos] > expected_bub[pos] + tolerance
        min_pos_p = p_arr[pos]
        p_arr[pos] = (p_arr[pos] + max_pos_p) / 2.0
      elsif expected_simbub[pos] < expected_bub[pos] - tolerance
        max_pos_p = p_arr[pos]
        p_arr[pos] = (p_arr[pos] + min_pos_p) / 2.0
      else
        break
      end
    end
  end
  return p_arr
end


def compare(n, p, p_arr, trials)
  expected_bub = get_expected_bub(n, p)
  #bub_totals = [0]*n
  simbub_totals = [0]*n
  trials.times do 
    simbub_trial = get_simbub(n, p_arr, 0)
    #bub_trial = bub(n, p)
    0.upto(n-1) do |i|
      simbub_totals[i] += simbub_trial[i] 
      #bub_totals[i] += bub_trial[i]
    end
  end

  puts "   #:  expbub |  simbub |   delta"

  0.upto(n-1) do |i|
    #b = bub_totals[i] / trials.to_f
    b = expected_bub[i]
    s = simbub_totals[i] / trials.to_f
    puts "#{(i).to_s.rjust(4)}: #{b.round(2).to_s.rjust(7)} | #{s.round(2).to_s.rjust(7)} | #{(s-b).round(2).to_s.rjust(7)}"
  end
end


def time_trials(n, p, seconds)
  t = Time.now
  bub_counter = 0
  while Time.now < t + seconds do
    get_bub(n, p)
    bub_counter += 1
  end
  t = Time.now
  p_arr = get_p_arr(n, p, 0.0001)
  p_arr_seconds = Time.now - t
  t = Time.now
  simbub_counter = 0
  while Time.now < t + seconds do
    get_simbub(n, p_arr)
    simbub_counter += 1
  end
  puts "Trial results (#{seconds} seconds): "
  puts "Time to get p_arr for simbub: #{p_arr_seconds.round(2)}"
  puts "bub runs: #{bub_counter}"
  puts "simbub runs: #{simbub_counter}"
  puts "ratio: #{(simbub_counter.to_f/bub_counter.to_f).round(2)}"
end
计时赛:(simbub 60秒跑)/(bubble_shuffle 60秒跑)


我同意btilly和其他人的观点,这种相关性使这一点非常困难,如果不是不可能做到的话

关于你的方法,单道次的运动确实是几何分布的。但是,对于许多道次,中心极限定理开始起作用。忽略边界效应,在单道次中,元素以概率
p
向左移动一次,否则(以概率
(1-p)
)以成功概率
1-p
向右移动几何次数。此分布的平均值为零。第一种可能性为方差贡献
p(-1)^2=p
。第二种可能性为
(1-p)sum{i>=0}p^i(1-p)i^2
,Wolfram Alpha评估为
(1+p)p/(1-p)

假设方差为
v=p+(1+p)p/(1-p)
,我们可以想象
t
通过后元素的增量位置正态分布,平均值为零,标准偏差
sqrt(tv)
。我们的下一个近似值是从离散时间切换到连续时间,对于每个元素,提取一个标准样本
x
,并假设δ位置平滑地变化为
sqrt(tV)x
。对于最初位于
i
位置的元素,我们可以求解方程
i+sqrt(tV)x=n-t
表示
t
以近似该元素被锁定的时间。然后我们仅按
t
降序排序

这里有一个简短的Python程序实现了这一点。希望它足够接近

import math
import random


def variance(p):
    return p + (1 + p) * p / (1 - p)


def solve_quadratic(b, c):
    assert c < 0
    return (math.sqrt(b ** 2 - 4 * c) - b) / 2


def bubble_shuffle(arr, p):
    n = len(arr)
    s = math.sqrt(variance(p))
    return [
        arr[i]
        for i in sorted(
            range(n),
            key=lambda i: solve_quadratic(random.gauss(0, s), i - n),
            reverse=True,
        )
    ]


if __name__ == "__main__":
    print(bubble_shuffle(range(100), 0.5))
导入数学
随机输入
def差异(p):
返回p+(1+p)*p/(1-p)
def解算二次型(b,c):
断言c<0
返回(数学sqrt(b**2-4*c)-b)/2
def气泡_混洗(arr,p):
n=长度(arr)
s=数学sqrt(方差(p))
返回[
arr[i]
因为我在整理(
范围(n),
key=lambda i:解_二次型(随机高斯(0,s),i-n),
反向=真,
)
]
如果名称=“\uuuuu main\uuuuuuuu”:
打印(气泡洗牌(范围(100),0.5))

没有比
O(n^2)
更简单的方法了。我在兔子洞里走了一段路,决定如果有办法的话,我没有足够的智慧找到它。这就是我降落的地方。很抱歉浪费了你的时间,我希望你玩得开心。你能确认是否有(n,p)你只想做一次或多次?也就是说,对于给定的n&p,不管试验次数多少,都需要做一次O(n^2)的工作,但是每次试验都会更快。这会有趣吗?还有,具有类似特性但与气泡洗牌不同的方法有趣吗?@Dave
bubble\u suffle([1, 2, 3], 0.5)
不应该产生确定性输出,除非你在随机数生成上设置种子。关于类似的属性,是的,这肯定会很有趣,我认为最终这就是这个问题的最佳答案所在,因为我不认为会出现“更好”的答案,如你下面的答案。我将仔细看。@poulter7我知道你不想要确定性的输出。我的意思是,对于给定的(p,n),如果在O(n^2)时间内我能产生一个p
         p=0.01  p=0.25  p=0.50  p=0.75  p=0.99
n = 100   10.85   10.17   10.75    9.53    4.16
n = 200   22.98   18.11   17.30   13.46    5.33 
n = 300   27.70   25.03   23.88   18.11    5.94
n = 400   41.09   29.46   27.11   21.81    6.92
import math
import random


def variance(p):
    return p + (1 + p) * p / (1 - p)


def solve_quadratic(b, c):
    assert c < 0
    return (math.sqrt(b ** 2 - 4 * c) - b) / 2


def bubble_shuffle(arr, p):
    n = len(arr)
    s = math.sqrt(variance(p))
    return [
        arr[i]
        for i in sorted(
            range(n),
            key=lambda i: solve_quadratic(random.gauss(0, s), i - n),
            reverse=True,
        )
    ]


if __name__ == "__main__":
    print(bubble_shuffle(range(100), 0.5))