字节数组操作 - 访谈问题

时间:2011-03-23 21:14:50

标签: c# java bytearray puzzle

我和我的朋友进行了这次讨论,他在面试中向他询问了这个问题。问题是这样的。写一个函数,它接受一个字节数组(2维)作为输入和一个整数n,初始假设是M * N字节数组的所有元素都是零,问题是用值填充'n'字节数组元素1,例如,如果M = 5且N = 5且n值为10,则字节数组应为10/25个元素为1,其余15个值为0.填充值应为随机且一个单元格在字节数组中应该只填充一次。我很着迷尝试自己解决这个问题。我附上了迄今为止我提出的代码。

public Boolean ByteArrayFiller(int a,int b, int n)
    {
        int count = n;
        int iLocalCount = 0;
        byte[,] bArray= new byte[a,b];
        for (int i = 0; i <a; i++)
            for (int j = 1; j <b; j++)
                bArray[i, j] = 0;

        Random randa=  new Random();
        int iRandA = randa.Next(a);
        int iRandB = randa.Next(b);

        while (iLocalCount < n)
        {
            if (bArray[iRandA, iRandB] == 0)
            {
                bArray[iRandA, iRandB] = 1;
                iLocalCount++;
            }

            iRandA = randa.Next(a);
            iRandB = randa.Next(b);
            continue;
        }
        //do
        //{
        //     //iRandA = randa.Next(a);
        //     //iRandB = randa.Next(b);
        //    bArray[iRandA,iRandB]=1;
        //    iLocalCount++;

        //} while (iLocalCount<=count && bArray[iRandA,iRandB]==0);

        return true;
    }

我写的代码是在C#中,但它很容易理解。它能够完成问题的目的(我做了一些试运行并且结果正确地完成了)但我在C#中使用了Random对象(相当于Java中的Math.Rand)来填充字节数组,我一直在想Rand为a和b返回相同的值。这是一个很好的机会无限期地去。这是问题的目的吗?或者我为这个问题提出的解决方案是否足够好!

我很想知道这里的专家如何解决这个问题?我只是在寻找新的想法来拓展我的视野。任何指针都将非常感激。感谢您抽出宝贵时间阅读这篇文章!

4 个答案:

答案 0 :(得分:3)

尝试随机位置的while循环直到找到一个好位置通常是一种非常糟糕的方法。如果n = M * N,则最后一个将具有找到匹配的1 /(M * N)的概率。如果M * N足够大,则效率极低。

如果M * N不是太大,我会创建一个M * N大小的临时数组,用数字0到(M * N)-1填充它,然后置换它 - 即你走过它并且将当前值与随机其他值交换。

然后转到数组中的前n个元素并设置适当的单元格。 (row = value / columns,col = value%columns)。

答案 1 :(得分:2)

我会将逻辑上的数组视为一维数组。用规定的值填充前n个位置,然后将阵列洗牌。

给定一个字节数组,以及数组中的行数和列数,并假设该数组已经用0填充:

int NumElements = NumRows * NumCols;

for (int i = 0; i < NumElementsToFill; ++i)
{
    int row = i / NumRows;
    int col = i % NumCols;
    array[row, col] = 1;
}

// Now shuffle the array
Random rnd = new Random();

for (int i = 0; i < NumElements; ++i)
{
    int irow = i / NumRows;
    int icol = i % NumCols;

    int swapWith = rnd.Next(i+1);
    int swapRow = swapWith / NumRows;
    int swapCol = swapWith % NumCols;

    byte temp = array[irow, icol];
    array[irow, icol] = array[swapRow, swapCol];
    array[swapRow, swapCol] = temp;
}

这里的关键是将一维索引转换为行/列值。我使用了/%。您也可以使用Math.DivRem。或者创建为您设置get和set的Action方法。

答案 2 :(得分:0)

选择一个数字,该数字大于N和M并且是素数(或者是N和M的共同素数)。我们将此号码称为p

循环,直到您设置x个数字:

  1. 生成小于N * M的随机数。拨打这个号码`l`。
  2. 如果没有设置该位置,那么下一个放置数字的地方将是'p * l%(N * M)`。
  3. 这种方法的缺点是如果阵列填满,你将会有更多的碰撞。

答案 3 :(得分:0)

基本上,您需要从范围[0,p)(其中p = M * N)中选择n个唯一的随机数,并将它们映射到二维数组的位置。

天真的方法是1)通过重试生成非唯一数字2)用0到p-1之间的数字填充数组,将其洗牌并取前n个数字(需要O(p)时间,O(p)内存)

另一种方法是使用以下算法(O(n 2 )时间,O(n)内存,Java代码)选择它们:

public Set<Integer> chooseUniqueRandomNumbers(int n, int p) {
    Set<Integer> choosen = new TreeSet<Integer>();
    Random rnd = new Random();

    for (int i = 0; i < n; i++) {
        // Generate random number from range [0, p - i)
        int c = rnd.nextInt(p - i);

        // Adjust it as it was choosen from range [0, p) excluding already choosen numbers
        Iterator<Integer> it = choosen.iterator();
        while (it.hasNext() && it.next() <= c) c++;

        choosen.add(c);
    }

    return choosen;
}

将生成的数字映射到二维数组的位置是微不足道的。