语言不可知的洗牌卡片问题

时间:2008-09-11 10:27:15

标签: language-agnostic

不久前我在接受采访时,需要解决两个非常有趣的问题。我很好奇你会如何处理解决方案。

问题1:

除当前

以外的所有内容的产品

编写一个函数,将两个长度为len,input和index的整数数组作为输入,并生成第三个数组result,这样: result [i] =输入中所有内容的乘积,输入[index [i]]

除外

例如,如果使用len = 4,input = {2,3,4,5}和index = {1,3,2,0}调用该函数,则结果将设置为{40, 24,30,60}。

重要提示:您的算法必须以线性时间运行。

问题2 :(主题在Jeff的一篇帖子中)

均匀洗牌卡片

  1. 设计(在C ++或C#中)一个类Deck来代表一张有序的牌组,其中牌组包含52张牌,分为13个等级(A,2,3,4,5,6,四件套装的7,8,9,10,J,Q,K):黑桃(?),心形(?),钻石(?)和球杆(?)。

  2. 基于这个类,设计并实现一个有效的算法来洗牌一副牌。卡片必须均匀洗牌,也就是说,原始牌组中的每张牌必须具有相同的概率才能最终进入洗牌牌组的任何可能位置。 该算法应该在Deck类的方法shuffle()中实现: void shuffle()

  3. 算法的复杂程度是什么(作为卡片中卡片数n的函数)?

  4. 解释如何测试你的方法均匀洗牌(黑盒测试)。

  5. P.S。我有两个小时来编写解决方案

15 个答案:

答案 0 :(得分:6)

第一个问题:

int countZeroes (int[] vec) {
int ret = 0;
foreach(int i in vec) if (i == 0) ret++;

return ret;
}

int[] mysticCalc(int[] values, int[] indexes) {
    int zeroes = countZeroes(values); 
    int[] retval = new int[values.length];
    int product = 1;

    if (zeroes >= 2) { // 2 or more zeroes, all results will be 0
        for (int i = 0; i > values.length; i++) {
            retval[i] = 0;      
        }
        return retval;
    }
    foreach (int i in values) {
        if (i != 0) product *= i; // we have at most 1 zero, dont include in product;
    }
    int indexcounter = 0;
    foreach(int idx in indexes) {
        if (zeroes == 1 && values[idx] != 0) {  // One zero on other index. Our value will be 0
            retval[indexcounter] = 0;
        }
        else if (zeroes == 1) { // One zero on this index. result is product
            retval[indexcounter] = product;
        }
        else { // No zeros. Return product/value at index
            retval[indexcounter] = product / values[idx];
        }
        indexcouter++;
    }   
    return retval;
}

最坏的情况是,该程序将逐步执行3个向量。

答案 1 :(得分:3)

除Python当前的所有内容的产品

from numpy import array

def product(input, index):
    a = array(input)[index]

    if a[a == 0].size != 1:
        a = a.prod() / a # product except current
    else:
        # exaclty one non-zero-valued element in `a`
        nzi = a.nonzero() # indices of non-zero-valued elements
        a[a == 0] = a[nzi].prod()
        a[nzi] = 0

    return a

示例:

for input in ([2,3,4,5], [2,0,4,5], [0,3,0,5]):
    print product(input, [1,3,2,0]) 

输出:

[40 24 30 60]
[40  0  0  0]
[0 0 0 0]

答案 2 :(得分:1)

对于第一个,首先计算输入的全部内容的乘积,然后对于索引的每个元素,通过输入[index [i]]除以计算的乘积,以填充结果数组。

当然我必须假设输入没有零。

答案 3 :(得分:1)

除C中的当前内容以外的所有产品

void product_except_current(int input[], int index[], int out[], 
                            int len) {
  int prod = 1, nzeros = 0, izero = -1;

  for (int i = 0; i < len; ++i) 
    if ((out[i] = input[index[i]]) != 0)
      // compute product of non-zero elements 
      prod *= out[i]; // ignore possible overflow problem
    else {
      if (++nzeros == 2) 
         // if number of zeros greater than 1 then out[i] = 0 for all i
         break; 
      izero = i; // save index of zero-valued element
    }

  //  
  for (int i = 0; i < len; ++i)  
    out[i] = nzeros ? 0 : prod / out[i];                               

  if (nzeros == 1)
    out[izero] = prod; // the only non-zero-valued element
}

答案 4 :(得分:1)

第一个问题在C#3中的线性时间解决方案是: -

IEnumerable<int> ProductExcept(List<int> l, List<int> indexes) {
    if (l.Count(i => i == 0) == 1) {
        int singleZeroProd = l.Aggregate(1, (x, y) => y != 0 ? x * y : x);
        return from i in indexes select l[i] == 0 ? singleZeroProd : 0;
    } else {
        int prod = l.Aggregate(1, (x, y) => x * y);
        return from i in indexes select prod == 0 ? 0 : prod / l[i];
    }
}

修改:考虑单个零!!我工作时的最后一个解决方案花了我2分钟,所以我感觉不是很糟糕: - )

答案 5 :(得分:1)

以下是使用测试方法在C#中的第二个答案。 Shuffle向我看O(n)。

编辑:看了Fisher-Yates洗牌后,我发现我在不知情的情况下重新发明了这个算法:-)但是很明显。我实施了Durstenfeld方法,它将我们从O(n ^ 2) - > O(n),非常聪明!

public enum CardValue { A, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, J, Q, K }
public enum Suit { Spades, Hearts, Diamonds, Clubs }

public class Card {
    public Card(CardValue value, Suit suit) {
        Value = value;
        Suit = suit;
    }

    public CardValue Value { get; private set; }
    public Suit Suit { get; private set; }
}

public class Deck : IEnumerable<Card> {
    public Deck() {
        initialiseDeck();
        Shuffle();
    }

    private Card[] cards = new Card[52];

    private void initialiseDeck() {
        for (int i = 0; i < 4; ++i) {
            for (int j = 0; j < 13; ++j) {
                cards[i * 13 + j] = new Card((CardValue)j, (Suit)i);
            }
        }
    }

    public void Shuffle() {
        Random random = new Random();

        for (int i = 0; i < 52; ++i) {
            int j = random.Next(51 - i);
            // Swap the cards.
            Card temp = cards[51 - i];
            cards[51 - i] = cards[j];
            cards[j] = temp;
        }
    }

    public IEnumerator<Card> GetEnumerator() {
        foreach (Card c in cards) yield return c;
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
        foreach (Card c in cards) yield return c;
    }
}

class Program {
    static void Main(string[] args) {
        foreach (Card c in new Deck()) {
            Console.WriteLine("{0} of {1}", c.Value, c.Suit);
        }

        Console.ReadKey(true);
    }
}

答案 6 :(得分:1)

在Haskell:

import Array

problem1 input index = [(left!i) * (right!(i+1)) | i <- index]
  where left  = scanWith scanl
        right = scanWith scanr
        scanWith scan = listArray (0, length input) (scan (*) 1 input)

答案 7 :(得分:0)

不幸的是,Vaibhav不得不假设输入表中可能有0。

答案 8 :(得分:0)

Tnilsson,很棒的解决方案(因为我完全按照相同的方式完成了它:P)。

我没有看到任何其他方式在线性时间内这样做。有人吗?因为招聘经理告诉我,这个解决方案不够强大。

我们是否遗漏了一些超级复杂的东西,在一条回程中做所有事情,解决方案?

答案 9 :(得分:0)

第二个问题。

    public static void shuffle (int[] array) 
    {
        Random rng = new Random();   // i.e., java.util.Random.
        int n = array.length;        // The number of items left to shuffle (loop invariant).
        while (n > 1) 
        {
            int k = rng.nextInt(n);  // 0 <= k < n.
            n--;                     // n is now the last pertinent index;
            int temp = array[n];     // swap array[n] with array[k] (does nothing if k == n).
            array[n] = array[k];
            array[k] = temp;
        }
    }

这是关于Fisher-Yates shuffle的维基百科文章的复制/粘贴。 O(n)复杂性

答案 10 :(得分:0)

Tnilsson,我同意YXJuLnphcnQ解决方案可以说更快,但是同样的想法。我忘了补充说,语言在第一个问题中是可选的,在第二个问题中也是可选的。

你是对的,计算零,并且同一循环中的产品更好。也许就是这样。

答案 11 :(得分:0)

答案 12 :(得分:0)

在C ++

中均匀地随机播放卡片组
#include <algorithm>

class Deck {
  // each card is 8-bit: 4-bit for suit, 4-bit for value
  // suits and values are extracted using bit-magic
  char cards[52];
  public:
  // ...
  void shuffle() {
    std::random_shuffle(cards, cards + 52);
  }
  // ...
};

复杂性:N中的线性。完全执行51次交换。见http://www.sgi.com/tech/stl/random_shuffle.html

<强>测试

  // ...
  int main() {
    typedef std::map<std::pair<size_t, Deck::value_type>, size_t> Map;
    Map freqs;    
    Deck d;
    const size_t ntests = 100000;

    // compute frequencies of events: card at position
    for (size_t i = 0; i < ntests; ++i) {
      d.shuffle();
      size_t pos = 0;
      for(Deck::const_iterator j = d.begin(); j != d.end(); ++j, ++pos) 
        ++freqs[std::make_pair(pos, *j)]; 
    }

    // if Deck.shuffle() is correct then all frequencies must be similar
    for (Map::const_iterator j = freqs.begin(); j != freqs.end(); ++j)
      std::cout << "pos=" << j->first.first << " card=" << j->first.second 
                << " freq=" << j->second << std::endl;    
  }

像往常一样,一项测试是不够的。

答案 13 :(得分:0)

Trilsson单独提出了关于问题测试部分的话题

How to test randomness (case in point - Shuffling)

非常好的主意Trilsson:)

答案 14 :(得分:0)

YXJuLnphcnQ,这就是我做的方式。这是最明显的。

但事实是,如果你编写一个算法,那么每次调用sort()时,只需将集合中的所有卡片向右移动一个位置,即使输出不是随机的,它也会通过测试。