【问题标题】:Generate a matrix of transition probabilities for bit strings of given size following some probability distribution按照某种概率分布为给定大小的位串生成转移概率矩阵
【发布时间】:2022-06-10 17:03:55
【问题描述】:

我想创建一个 8x8 矩阵来提供位通信中的错误概率。矩阵如下:

列表示观察量,行表示测量量。一个元素p[i,j] 等于条件概率p(j|i)。例如,元素 p[0,1] 给出当实际值为000 时观察字符串001 的概率,即它测量p(001|000)

问题:如何在 Python 中创建这样的矩阵,以便

  1. 位翻转越多,等效条件概率越小(例如p(100|000)<p(110|000)?
  2. 如何启用“不对称”。即p(001|000)< p(000|001) 的概率。也就是说,与从 0 到 1 的转换相比,具有更高概率的转换 1 到 0 的偏差。

当然,每一行的概率之和必须等于1。

总而言之,我想在 Python 中创建一个函数,该函数将整数 n(矩阵的大小,或者等效地,其中 2^n 是位串的长度)作为输入,并输出一个概率转换矩阵符合上述规定。

困难在于如何实现一个概率分布来填充单元格。

创建一个 8x8 数组并填充对角线很简单:

P = np.zeros((8,8))
for i in range(8):
    for j in range(8):
        if i==j:
            P[i,j]=1

同样,用固定数字填充给定行或给定列也很简单。但是,我无法弄清楚(甚至如何开始)按照上述规则填充这样的矩阵,甚至无法准确定义元素必须遵循的分布。

【问题讨论】:

  • 您可以轻松地填充矩阵一旦您确定了 0->1 和 1->0 错误的概率,它是什么?
  • 抱歉,我不确定我是否理解了这个问题。
  • 让我换个方式问这个问题。你有什么信息作为生成矩阵的输入(除了它的大小 n)?
  • 在对角线上生成一个矩阵实际上要简单得多:np.eye(8)
  • @mozway 这是一个我想保留的参数,称之为b,作为偏差。所以输入是n,b

标签: python numpy probability distribution


【解决方案1】:

事实证明,您无需numpyscipy 就可以做到这一点。我使用pandas 进行漂亮的打印。

逻辑是,对于每个位,您都有可能翻转(p01p10)或保持不变(p00p11)。将一个位串转换为另一个位串需要将每个n 位的适当概率相乘。

例如:P(010|001) = P(0->0) * P(1->0) * P(0->1) = p00 * p10 * p01

每个sentobserved 组合都会重复此过程。

您可以使用nested ternary assignment 将下面的两级if 语句进一步减少为一行,但我认为这是简洁易读的一个很好的平衡:

import pandas as pd


def p(sent, observed, p01, p10):
    """Return the probability of 'sent' being received as 'observed'
    given p01 (the probability a bit flips from a 0->1) and p10 (the
    probability a bit flips from 1->0).
    """
    p00 = 1 - p01
    p11 = 1 - p10
    r = 1
    for i, _ in enumerate(sent):
        if sent[i] == "0":
            r *= p00 if observed[i] == "0" else p01
        else:
            r *= p10 if observed[i] == "0" else p11
    return r

def generate_error_matrix(n, p01, p10):
    """Print a matrix of the transitions of all permutations of bit
    errors for a given bit length.

    Parameters:
        n - the number of bits
        p01 - probability of a bit flipping from 0 to 1
        p10 - probability of a bit flipping from 1 to 0
    """
    labels = [f"{i:0{n}b}" for i in range(0, 2**n)]
    result = pd.DataFrame(index=labels, columns=labels)
    for rowIndex, row in result.iterrows():
        for columnIndex, _ in row.items():
            result.at[rowIndex, columnIndex] = p(rowIndex, columnIndex, p01, p10)
    return result

这是一个例子:

print(generate_error_matrix(n=3, p01=0.2, p10=0.1))
       000    001    010    011    100    101    110    111
000  0.512  0.128  0.128  0.032  0.128  0.032  0.032  0.008
001  0.064  0.576  0.016  0.144  0.016  0.144  0.004  0.036
010  0.064  0.016  0.576  0.144  0.016  0.004  0.144  0.036
011  0.008  0.072  0.072  0.648  0.002  0.018  0.018  0.162
100  0.064  0.016  0.016  0.004  0.576  0.144  0.144  0.036
101  0.008  0.072  0.002  0.018  0.072  0.648  0.018  0.162
110  0.008  0.002  0.072  0.018  0.072  0.018  0.648  0.162
111  0.001  0.009  0.009  0.081  0.009  0.081  0.081  0.729

还有一些极端情况:

0 总是会变成 1,1 永远不会变成 0:

print(generate_error_matrix(n=3, p01=1, p10=0))
    000 001 010 011 100 101 110 111
000   0   0   0   0   0   0   0   1
001   0   0   0   0   0   0   0   1
010   0   0   0   0   0   0   0   1
011   0   0   0   0   0   0   0   1
100   0   0   0   0   0   0   0   1
101   0   0   0   0   0   0   0   1
110   0   0   0   0   0   0   0   1
111   0   0   0   0   0   0   0   1

1 总是翻转为零,0 永远不会翻转为 1:

print(generate_error_matrix(n=3, p01=0, p10=1))
    000 001 010 011 100 101 110 111
000   1   0   0   0   0   0   0   0
001   1   0   0   0   0   0   0   0
010   1   0   0   0   0   0   0   0
011   1   0   0   0   0   0   0   0
100   1   0   0   0   0   0   0   0
101   1   0   0   0   0   0   0   0
110   1   0   0   0   0   0   0   0
111   1   0   0   0   0   0   0   0

位总是翻转:

print(generate_error_matrix(n=3, p01=1, p10=1))
    000 001 010 011 100 101 110 111
000   0   0   0   0   0   0   0   1
001   0   0   0   0   0   0   1   0
010   0   0   0   0   0   1   0   0
011   0   0   0   0   1   0   0   0
100   0   0   0   1   0   0   0   0
101   0   0   1   0   0   0   0   0
110   0   1   0   0   0   0   0   0
111   1   0   0   0   0   0   0   0

无论方向如何,每一位都有 50% 的机会翻转:

print(generate_error_matrix(n=3, p01=0.5, p10=0.5))
       000    001    010    011    100    101    110    111
000  0.125  0.125  0.125  0.125  0.125  0.125  0.125  0.125
001  0.125  0.125  0.125  0.125  0.125  0.125  0.125  0.125
010  0.125  0.125  0.125  0.125  0.125  0.125  0.125  0.125
011  0.125  0.125  0.125  0.125  0.125  0.125  0.125  0.125
100  0.125  0.125  0.125  0.125  0.125  0.125  0.125  0.125
101  0.125  0.125  0.125  0.125  0.125  0.125  0.125  0.125
110  0.125  0.125  0.125  0.125  0.125  0.125  0.125  0.125
111  0.125  0.125  0.125  0.125  0.125  0.125  0.125  0.125

【讨论】:

  • 我认为这种方法是错误的,因为它使用了 n 位翻转的概率,而与起始位无关。例如,00 转换概率应仅取决于p01,因为没有 1 可以翻转。同样,11 转换概率应仅取决于p10,因为没有 0 可以翻转。此外,概率质量分布仅取决于事件的数量,并且它结合了具有相同数量但不同顺序的位翻转:00 -> 1000 -> 01 转换状态都同意pmf 表示一个 0 翻转为 1,这没有得到适当的考虑。
  • 这个想法是正确的,但这不是代码正在做的事情:result.at[rowIndex, columnIndex] = pmf01[i] * pmf10[j] 使用 pmf10 即使是 000xxx 的转换,它不应该因为没有1 开始。
  • 此外,pmf 为您提供给定概率的 n 个可能事件中发生 x 个事件的概率。当你从混合状态开始时,比如说00111,有两个0和三个1,所以你应该使用pmf01n == 2pmf10开始n == 3,并确保你加权正确组合(除以相应的二项式系数),因为例如pmf01(1, 2, p) 结合了000110 的概率。
  • @norok2 我已经更新了我的答案以获得更简单的解决方案。
  • 现在看起来是正确的,但与更优化的方法相比,它会相对较慢(几个数量级)。
【解决方案2】:

与值和位置无关的位转换

可以在多种情况下计算某些位状态转换到另一个位状态的概率。

最简单的一种是当某个位转换到不同状态的给定概率p 时,这与位值、位在位状态中的位置以及其他位转换无关.

当然,位不翻转的概率由q == 1 - p给出。

n有两个结果的独立事件的统计数据为studied extensively。)

对于更多位,可以通过乘法组合多个位转换的概率。

ab 的转换概率(其中ab 是相同长度的两个位配置n)取决于位转换t_ab 和非转换的数量s_ab == n - t_ab:

p(a, b) == (p ** t_ab) * (q ** s_ab)

例如,转换:0b000110b00101 由以下公式给出:

p(0b00011, 0b00101) == (q ** 3) * (p ** 2)

请注意,这与例如0b0110b101 的转换概率,因为要考虑的位数起作用。

给定一个计算数字中1个数的函数:

def count_set_bits(num):
    result = 0
    while num:
        result += num & 1
        num >>= 1
    return result

计算t 的一种简单方法是通过xor 运算符:

t = count_set_bits(a ^ b)

因此,可以通过简单的循环“手动”计算转移概率矩阵w_bits

除非加速显式循环,否则计算速度非常慢。 此用例最简单的加速之一是使用Numba。 所有_nb-ending 函数都使用它加速。 可以将 fastmath 标志 nb.njit(fastmath=True) 设置为可能将执行时间减少几个百分点。

import numpy as np
import numba as nb


@nb.njit
def count_set_bits(num):
    result = 0
    while num:
        result += num & 1
        num >>= 1
    return result


@nb.njit
def w_bits_sym_cb_nb(n, p=0.2):
    if n > 0:
        q = 1 - p
        m = 2 ** n
        result = np.empty((m, m), dtype=np.float_)
        for i in range(m):
            for j in range(i + 1):
                t = count_set_bits_nb(i ^ j)
                s = n - t
                result[i, j] = result[j, i] = (p ** t) * (q ** s)
        return result
    else:
        return np.empty((0, 0))

(请注意,count_set_bits() 也已加速)。

或者,可以通过重复 1 位情况的基本概率矩阵来构造逐元素乘法概率矩阵:

  0 1
0 q p
1 p q

具有两次重复的幂,例如两个字节:

q p q p     q q p p
p q p q  X  q q p p
q p q p     p p q q  
p q p q     p p q q

这可以再次通过“手动”循环计算:

@nb.njit
def w_bits_sym_lm_nb(n, p=0.2):
    if n > 0:
        b = 2
        m = b ** n
        q = 1 - p
        base = np.array([[q, p], [p, q]])
        result = np.ones((m, m), dtype=base.dtype)
        for k in range(n):
            bk = (b ** k)
            for i in range(m):
                for j in range(m):
                    result[i, j] *= base[i // bk % b, j // bk % b]
        return result
    else:
        return np.empty((0, 0))

但是,一种更快的方法是使用广播乘法执行与重复元素的逐元素矩阵乘法(@PierreD's answer 的完善版本):

import numpy as np


def bc_mul(a, b):
    nm = len(a) * len(b)
    return (a[:, None, :, None] * b[None, :, None, :]).reshape(nm, nm)


def w_bits_sym_bm(n, p=0.2):
    if n > 0:
        base = np.array([[1 - p, p], [p, 1 - p]])
        result = base.copy()
        for i in range(1, n):
            result = bc_mul(base, result)
        return result
    else:
        return np.empty((0, 0))

请注意,由于bc_mul() 是关联的,因此可以将循环内的行写为result = bc_mul(base, result)result = bc_mul(result, base),但性能却截然不同!

最后一种方法也相当快,尤其是对于较大的n 渐近,主要是因为它执行的乘法次数呈指数级减少。

同样可以用 Numba 重写,但性能相似(但性能稍慢):

@nb.njit
def bc_mul_nb(a, b):
    n = len(a)
    m = len(b)
    nm = n * m
    result = np.empty((nm, nm), dtype=a.dtype)
    for i in range(n):
        for j in range(m):
            for k in range(n):
                for l in range(m):
                    result[i * m + j, k * m + l] = a[i, k] * b[j, l]
    return result


@nb.njit
def w_bits_sym_bm_nb(n, p=0.2):
    if n > 0:
        base = np.array([[1 - p, p], [p, 1 - p]])
        result = base.copy()
        for i in range(1, n):
            result = bc_mul_nb(base, result)
        return result
    else:
        return np.empty((0, 0))

更多关于执行速度(包括基准)的信息如下。


值相关/位置无关位转换

一个稍微复杂,更有趣的场景场景是当 0 到 1 和 1 到 0 的概率不同,但仍然独立于位置等时。

两者都可以从base概率矩阵计算:

    0   1
0 p00 p01
1 p10 p11

其中p00p01p10p11 是一位从一种状态转换到另一种状态的概率。

当然:

  • p00 == 1 - p01
  • p11 == 1 - p10

和以前一样,对于更多位,可以通过乘法组合多个位转换的概率。

这本质上是上述的不对称版本。

ab 的转换概率(其中ab 是相同长度的两位配置)取决于转换的数量t00_abt01_abt10_ab , t11_ab 乘以它们各自的概率(对称情况下使用的符号,t01t10 对应于 tt00t11 对应于 s):

p(a, b) == (
    (p00 ** t00_ab) *
    (p01 ** t01_ab) *
    (p10 ** t10_ab) *
    (p11 ** t11_ab))

例如,转换:0b000110b00101 由以下公式给出:

p(0b00011, 0b00101) == (p00 ** 2) * (p01 ** 1) * (p10 ** 1) * (p11 ** 1)

当然,所有这些都可以与上述类似的计算。 设置位计数方法可以直接在~a & ba & ~b 上与a & b 一起使用来计数位转换:

@nb.njit
def w_bits_cb_nb(n, p01=0.2, p10=-1):
    if n > 0:
        p10 = p10 if p10 >= 0 else p01
        p00 = 1 - p01
        p11 = 1 - p10
        m = 2 ** n
        result = np.empty((m, m), dtype=np.float_)
        for i in range(m):
            for j in range(m):
                t11 = count_set_bits_nb(i & j)
                t01 = count_set_bits_nb(~i & j)
                t10 = count_set_bits_nb(i & ~j)
                t00 = n - (t11 + t01 + t10)
                result[i, j] = \
                    (p00 ** t00) * (p11 ** t11) * (p01 ** t01) * (p10 ** t10)
        return result
    else:
        return np.empty((0, 0))

或者可以在单个循环中稍微更有效地完成(与@Viglione's current answer 中的类似但更快):

@nb.njit
def bit_diff_nb(a, b, n):
    t11 = t01 = t10 = 0
    t00 = n
    while a | b:
        aa = a & 1
        bb = b & 1
        t11 += aa & bb
        t01 += ~aa & bb
        t10 += aa & ~bb
        a >>= 1
        b >>= 1
    t00 = n - (t11 + t01 + t10)
    return t00, t11, t01, t10


@nb.njit
def w_bits_bd_nb(n, p01=0.2, p10=-1):
    if n > 0:
        p10 = p10 if p10 >= 0 else p01
        p00 = 1 - p01
        p11 = 1 - p10
        m = 2 ** n
        result = np.empty((m, m), dtype=np.float_)
        for i in range(m):
            for j in range(m):
                t00, t11, t01, t10 = bit_diff_nb(i, j, n)
                result[i, j] = \
                    (p00 ** t00) * (p11 ** t11) * (p01 ** t01) * (p10 ** t10)
        return result
    else:
        return np.empty((0, 0))

另外,所有其他方法都可以轻松扩展到这种情况:

@nb.njit
def w_bits_lm_nb(n, p01=0.2, p10=-1):
    if n > 0:
        p10 = p10 if p10 >= 0 else p01
        b = 2
        m = b ** n
        base = np.array([[1 - p01, p01], [p10, 1 - p10]])
        result = np.ones((m, m), dtype=base.dtype)
        for k in range(n):
            bk = (b ** k)
            for i in range(m):
                for j in range(m):
                    result[i, j] *= base[i // bk % b, j // bk % b]
        return result
    else:
        return np.empty((0, 0))
def w_bits_bm(n, p01=0.1, p10=-1):
    if n > 0:
        p10 = p10 if p10 >= 0.0 else p01
        base = np.array([[1 - p01, p01], [p10, 1 - p10]])
        result = base.copy()
        for i in range(1, n):
            result = bc_mul(base, result)
        return result
    else:
        return np.empty((0, 0))
def w_bits_bmi(n, p01=0.1, p10=-1):
    if n > 0:
        p10 = p10 if p10 >= 0.0 else p01
        base = np.array([[1 - p01, p01], [p10, 1 - p10]])
        result = base.copy()
        for i in range(1, n):
            result = bc_mul(result, base)
        return result
    else:
        return np.empty((0, 0))

结果一致性

为了完整起见,我还包含了currently accepted and top voted answer 方法(类似于w_bits_bd_nb(),但使用二进制字符串且没有加速)和一些桥接代码来获取底层 NumPy 数组:

import pandas as pd


def calc_p(sent, observed, p01, p10):
    p00 = 1 - p01
    p11 = 1 - p10
    r = 1
    for i, _ in enumerate(sent):
        if sent[i] == "0":
            r *= p00 if observed[i] == "0" else p01
        else:
            r *= p10 if observed[i] == "0" else p11
    return r


def generate_error_matrix(n, p01, p10):
    labels = [f"{i:0{n}b}" for i in range(0, 2 ** n)]
    result = pd.DataFrame(index=labels, columns=labels)
    for rowIndex, row in result.iterrows():
        for columnIndex, _ in row.items():
            result.at[rowIndex, columnIndex] = calc_p(rowIndex, columnIndex, p01, p10)
    return result

    
def w_bits_bs_pd(n, p01=0.2, p10=-1):
    p10 = p10 if p10 >= 0.0 else p01
    return generate_error_matrix(n, p01, p10).to_numpy().astype(float)
funcs = (
    w_bits_bm, w_bits_bmi,
    w_bits_cb_nb, w_bits_bd_nb, w_bits_lm_nb,
    w_bits_bm_nb, w_bits_bmi_nb,
    w_bits_sym_cb_nb, w_bits_sym_bm_nb, w_bits_sym_lm_nb,
    w_bits_bs_pd)


n = 2
base = funcs[0](n)
print(f"{'ProbRowsSumTo1:':>27} {np.allclose(np.sum(base, 0), np.ones(2 ** n))}")

x = w_bits_bm(10, 0.2, 0.2)
print(f"{'(p01 == p10) ->  Symmetric:':>27} {np.allclose(x, x.T)}")
x = w_bits_bm(10, 0.2, 0.4)
print(f"{'(p01 != p10) -> Asymmetric:':>27} {not np.allclose(x, x.T)}")
print()
for func in funcs:
    res = func(n)
    print(f"{func.__name__!s:>20}  Same: {np.allclose(base, res)}")
    print(func(2))
    print()
            ProbRowsSumTo1: True
(p01 == p10) ->  Symmetric: True
(p01 != p10) -> Asymmetric: True

           w_bits_bm  Same: True
[[0.64 0.16 0.16 0.04]
 [0.16 0.64 0.04 0.16]
 [0.16 0.04 0.64 0.16]
 [0.04 0.16 0.16 0.64]]

          w_bits_bmi  Same: True
[[0.64 0.16 0.16 0.04]
 [0.16 0.64 0.04 0.16]
 [0.16 0.04 0.64 0.16]
 [0.04 0.16 0.16 0.64]]
...

下面的代码表明:

  • 所有函数都给出相同的结果
  • 如果p01 == p10 转移矩阵是对称的
  • 如果p01 != p10 转移矩阵是不对称的
  • 所有行加起来为一(单独)

基准测试

由于大多数对称实现与非对称实现非常相似,因此它们已从基准测试中省略。

funcs = (
    w_bits_bm, w_bits_bmi,
    w_bits_cb_nb, w_bits_bd_nb, w_bits_lm_nb,
    w_bits_bm_nb, w_bits_bmi_nb,
    w_bits_sym_cb_nb, w_bits_bs_pd)

timings = {}
for n in range(1, 12):
    print(f"n = {n}")
    timings[n] = []
    base = funcs[0](n)
    for func in funcs:
        res = func(n)
        timed = %timeit -r 4 -n 8 -q -o func(n)
        timing = timed.best * 1e6
        timings[n].append(timing)
        print(f"{func.__name__:>24}  {np.allclose(base, res)}  {timing:10.3f} µs")

要绘制:

import pandas as pd


df = pd.DataFrame(data=timings, index=[func.__name__ for func in funcs]).transpose()
df.plot(marker='o', logy=True, xlabel='Num. bits n / #', ylabel='Best timing / µs')

制作:

这确实表明基于广播乘法的解决方案对于较大的n 而言是渐进的,性能最高,但总体上在所有尺度上都相当出色。

请注意,由于计算复杂度呈指数增长,因此时序已按 y 对数标度绘制。

另请注意,w_bits_bs_pd() 比其他的要慢几个数量级。


更好的输出

像往常一样,在处理表格/矩阵等众所周知的对象时,使用特定的工具会很有好处。

如果想要获得漂亮的输出,可以使用Pandas(类似于@Viglione's answer 中所做的)和Seaborn 以获得更好的可视化效果:

import pandas as pd
import seaborn as sns


def gen_bit_transitions(n, p01=0.2, p10=-1, func=w_bits_bm):
    data = func(n, p01, p10)
    labels = [f"{i:0{n}b}" for i in range(2**n)]
    return pd.DataFrame(data, index=labels, columns=labels)
df = gen_bit_transitions(3, 0.4, 0.2)
sns.set(rc={'figure.figsize': (8, 7)})
sns.heatmap(df, annot=True, vmin=0.0, vmax=1.0)

df = gen_bit_transitions(5, 0.4, 0.2)
sns.set(rc={'figure.figsize': (9, 8)})
sns.heatmap(df, annot=False, vmin=0.0, vmax=1.0)

【讨论】:

    【解决方案3】:

    如果位转换的概率仅取决于原始位值,而与位置无关(即P(xy|ab) == P(yx|ba),那么您可以简单地块乘一个转换概率内核:

    x 是一个 2x2 矩阵,这样x[i,j] 是在给定真实i 的情况下观察位j 的概率。即:

    x = [[a, b]
         [c, d]]
    

    2位概率矩阵为:

    x2 = [[a, a, b, b],          [[a, b, a, b],
          [a, a, b, b],    *      [c, d, c, d],
          [c, c, d, d],           [a, b, a, b],
          [c, c, d, d]]           [c, d, c, d]]
    

    这样的块乘法可以简单地用numpy表示:

    def bmul(a, x):
        n = a.shape[0] * x.shape[0]
        return (a[:, None, :, None] * x[None, :, None, :]).reshape(n, n)
    

    示例:

    u = .2  # "up": p(1|0)
    d = .1  # "down": p(0|1)
    x = np.array([[1-u, u], [d, 1-d]])
    
    >>> x
    array([[0.8, 0.2],
           [0.1, 0.9]])
    
    x2 = bmul(x, x)
    >>> x2
    array([[0.64, 0.16, 0.16, 0.04],
           [0.08, 0.72, 0.02, 0.18],
           [0.08, 0.02, 0.72, 0.18],
           [0.01, 0.09, 0.09, 0.81]])
    
    x3 = bmul(x2, x)
    >>> x3
    array([[0.512, 0.128, 0.128, 0.032, 0.128, 0.032, 0.032, 0.008],
           [0.064, 0.576, 0.016, 0.144, 0.016, 0.144, 0.004, 0.036],
           [0.064, 0.016, 0.576, 0.144, 0.016, 0.004, 0.144, 0.036],
           [0.008, 0.072, 0.072, 0.648, 0.002, 0.018, 0.018, 0.162],
           [0.064, 0.016, 0.016, 0.004, 0.576, 0.144, 0.144, 0.036],
           [0.008, 0.072, 0.002, 0.018, 0.072, 0.648, 0.018, 0.162],
           [0.008, 0.002, 0.072, 0.018, 0.072, 0.018, 0.648, 0.162],
           [0.001, 0.009, 0.009, 0.081, 0.009, 0.081, 0.081, 0.729]])
    

    最后一个值是您要查找的矩阵。

    随机抽查:

    # P(100|010) is u*d*(1-u), and we should find it in x3[4,2]
    >>> u * d * (1-u)
    0.016000000000000004
    
    >>> x3[4,2]
    0.016000000000000004
    

    有趣的事实:

    bmul 是关联的,但不是可交换的。换句话说:

    • bmul(bmul(a, b), c) == bmul(a, bmul(b, c),但是
    • bmul(a, b) != bmul(b, a)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-11
      • 2020-01-10
      • 2018-04-01
      • 2020-03-28
      • 2012-02-27
      • 1970-01-01
      相关资源
      最近更新 更多