Linq-获取数组中的连续数字

本文关键字:连续 数字 获取 数组 Linq- | 更新日期: 2023-09-27 18:20:10

我正在创建一个扑克系统,目前正在精简我的手计算器。

以下代码有效:

public enum CARDS
{
    None = 0,
    Two,
    Three,
    Four,
    Five,
    Six,
    Seven,
    Eight,
    Nine,
    Ten,
    Jack,
    Queen,
    King,
    Ace
};
public enum SUITS
{
    None = 0,
    Diamonds,
    Clubs,
    Hearts,
    Spades
};
public class Card
{
    public CARDS Val { get; set; }
    public SUITS Suit { get; set; }
}
public class IntIndex
{
    public int Count { get; set; }
    public int Index { get; set; }
}
static void Test()
{
    List<Card> cardList = new List<Card>();
    cardList.Add(new Card { Suit = SUITS.Diamonds, Val = CARDS.Two });
    cardList.Add(new Card { Suit = SUITS.Hearts, Val = CARDS.Four });
    cardList.Add(new Card { Suit = SUITS.Clubs, Val = CARDS.Five });
    cardList.Add(new Card { Suit = SUITS.Diamonds, Val = CARDS.Six });
    cardList.Add(new Card { Suit = SUITS.Spades, Val = CARDS.Six });
    cardList.Add(new Card { Suit = SUITS.Hearts, Val = CARDS.Seven });
    cardList.Add(new Card { Suit = SUITS.Clubs, Val = CARDS.Eight });
    // I have a processor that iterates through the above card list and creates
    // the following array based on the Card.Val as an index
    int[] list = new int[] {0,0,0,1,1,2,1,1,0,0,1,0,0,0};
    List<IntIndex> indexList =
        list.Select((item, index) => new IntIndex { Count = item, Index = index })
        .Where(c => c.Count > 0).ToList();
    List<int> newList = (from i in indexList
                         join j in indexList on i.Index equals j.Index + 1
                         where j.Count > 0
                         select i.Index).ToList();
    // Add the previous index since the join only works on n+1
    // Note - Is there a way to include the first comparison card?
    newList.Insert(0, newList[0] - 1);
    // Nice! - got my straight card list
    List<CARDS> cards = (from l in newList
                         select (CARDS)l).ToList();
}

然而,我想让它更紧凑,如:

static void Test()
{
    List<Card> cardList = new List<Card>();
    cardList.Add(new Card { Suit = SUITS.Diamonds, Val = CARDS.Two });
    cardList.Add(new Card { Suit = SUITS.Hearts, Val = CARDS.Four });
    cardList.Add(new Card { Suit = SUITS.Clubs, Val = CARDS.Five });
    cardList.Add(new Card { Suit = SUITS.Diamonds, Val = CARDS.Six });
    cardList.Add(new Card { Suit = SUITS.Spades, Val = CARDS.Six });
    cardList.Add(new Card { Suit = SUITS.Hearts, Val = CARDS.Seven });
    cardList.Add(new Card { Suit = SUITS.Clubs, Val = CARDS.Eight });
    List<Card> newList1 = (from i in cardList
                           join j in cardList on i.Val equals j.Val + 1
                           select i).ToList();
    // Add the previous index since the join only works on n+1
    // Similar to: newList1.Insert(0, newList1[0] - 1);
    // However, newList1 deals with Card objects so I need
    // To figure how to get the previous, non-duplicate card
    // from the original cardList (unless there is a way to return the
    // missing card!)
}

问题是Six正在重复。Distinct和自定义比较函数不起作用,因为这将破坏n+1联接子句。

另一个问题是以下卡片列表:

    List<Card> cardList = new List<Card>();
    cardList.Add(new Card { Suit = SUITS.Diamonds, Val = CARDS.Two });
    cardList.Add(new Card { Suit = SUITS.Hearts, Val = CARDS.Three });
    cardList.Add(new Card { Suit = SUITS.Clubs, Val = CARDS.Five });
    cardList.Add(new Card { Suit = SUITS.Diamonds, Val = CARDS.Six });
    cardList.Add(new Card { Suit = SUITS.Spades, Val = CARDS.Six });
    cardList.Add(new Card { Suit = SUITS.Hearts, Val = CARDS.Seven });
    cardList.Add(new Card { Suit = SUITS.Clubs, Val = CARDS.Eight });
    cardList.Add(new Card { Suit = SUITS.Diamonds, Val = CARDS.Jack });

我得到了一个3 Hearts、6 Diamond、7 Hearts和8 Hearts的返回列表,因为2和3是连续的。

我真正想要的是一个列表,它返回5张或更多的连续牌,或者更好的是,连续序列的前5张牌。因此,由于输入列表中没有5张连续的卡,因此上面的列表将返回空。

Linq-获取数组中的连续数字

如果您有兴趣从cardList中获得具有最高顺序卡值范围的卡的子集,无论是否适合,请考虑这种方法。

//Extension method to find a subset of sequential consecutive elements with at least the specified count of members.
//Comparisions are based on the field value in the selector.
//Quick implementation for purposes of the example...
//Ignores error and bounds checking for purposes of example.  
//Also assumes we are searching for descending consecutive sequential values.
public static IEnumerable<T> FindConsecutiveSequence<T>(this IEnumerable<T> sequence, Func<T, int> selector, int count)
{
    int start = 0;
    int end = 1;
    T prevElement = sequence.First();
    foreach (T element in sequence.Skip(1))
    {
        if (selector(element) + 1 == selector(prevElement))
        {
            end++;
            if (end - start == count)
            {
                return sequence.Skip(start).Take(count);
            }
        }
        else
        {
            start = end;
            end++;
        }
        prevElement = element;
    }
    return sequence.Take(0);
}

//Compares cards based on value alone, not suit.
//Again, ignores validation for purposes of quick example.
public class CardValueComparer : IEqualityComparer<Card>
{
    public bool Equals(Card x, Card y)
    {
        return x.Val == y.Val ? true : false;
    }
    public int GetHashCode(Card c)
    {
        return c.Val.GetHashCode();
    }
}

考虑到以上情况,方法是首先根据卡片的价值而不是套装对卡片进行排序,然后按降序排列。然后创建一个不同卡片的子集,同样只基于卡片的价值,而不是适合。然后调用FindConsecutiveSequence,指定用于比较的Val属性以及有效序列所需的元素数量。

//Sort in descending order based on value of the card.
cardList.Sort((x,y) => y.Val.CompareTo(x.Val));
//Create a subset of distinct card values.
var distinctCardSet = cardList.Distinct(new CardValueComparer());
//Create a subset of consecutive sequential cards based on value, with a minimum of 5 cards.
var sequentialCardSet = distinctCardSet.FindConsecutiveSequence(p => Convert.ToInt32(p.Val), 5);

我认为这应该涵盖你在问题中提出的问题,并给你一些可以建立的东西。然而,如果这是针对扑克的,那么在Ace可以是一个低值->{a,2,3,4,5}的情况下,这种逻辑将失败。我没有看到需要提到Ace特定的逻辑,所以也许你在问题的范围之外处理它。

按编号订购卡片,然后取1张并跳过其中的3张以选择您想要的卡片。

这就是您想要的吗?

首先是套装的随机列表,但卡片值的顺序列表

Random random = new Random((int)(DateTime.Now.ToBinary() % Int32.MaxValue));
List<Card> hand = new List<Card>();
for(int card = (int)CARDS.Five;card <= (int)CARDS.Nine;card++)
{
    SUIT suit = (SUITS)(random.Next(4)+1);
    hand.Add(new Card { Suit = suit, Val = (CARDS)card });
}

或者一个顺序的西装清单是…

for(int card = (int)CARDS.Five, int suit = (int)SUITS.Diamonds;card <= (int)CARDS.Nine;card++, suit++)
{
    if(suit > (int)SUITS.Spades)
        suit = (int)SUITS.Diamonds;
    hand.Add(new Card { Suit = (SUITS)suit, Val = (CARDS)card });
}

使用Aggregate方法。您可以修改下面的示例代码以返回不同长度的卡片列表,并更改while子句以检查必须匹配的卡片数量。(例如,我的版本检查计数==5,你可以检查计数>=5,等等)

public class Card {
    public CARDS Val { get; set; }
    public SUITS Suit { get; set; }
    // added ToString for program below
    public override string ToString() {
        return string.Format("{0} of {1}", Val, Suit);
    }
}
class Program {
    static IEnumerable<Card> RandomList(int size) {
        var r = new Random((int)DateTime.Now.Ticks);
        var list = new List<Card>();
        for (int i = 0; i < size; i++) {
            list.Add(new Card {
                Suit = (SUITS)r.Next((int)SUITS.Diamonds, (int)SUITS.Spades),
                Val = (CARDS)r.Next((int)CARDS.Two, (int)CARDS.Ace)
            });
        }
        return list.OrderBy(c => c.Val);
    }
    // generates a random list of 5 cards untill
    // the are in sequence, and then prints the
    // sequence
    static void Main(string[] args) {
        IEnumerable<Card> consecutive = null;
        do {
            // generate random list
            var hand = RandomList(5);
            // Aggreate:
            // the passed in function is run for each item
            // in hand. acc is the accumulator value.
            // It is passed in to each call. The new List<Card>()
            // parameter is the initial value of acc when the lambda
            // is called on the first item in the list
            // in the lambda we are checking to see if the last
            // card in the accumulator value is one less
            // than the current card. If so, add it to the
            // accumulator, otherwise do not.
            consecutive = hand.Aggregate(new List<Card>(), (acc, card) => {
                var size = acc.Count != 0
                    ? ((int)card.Val) - ((int)acc[acc.Count - 1].Val)
                    : 1;
                if (size == 1)
                    acc.Add(card);
                return acc;
            });
        } while (consecutive.Count() != 5);
        foreach (var card in consecutive) {
            Console.WriteLine(card);
        }
        Console.ReadLine();
    }
}

当提供七张牌(包括A-5的边缘情况)时,以下方法应该会获得最好的直接效果,但我还没有彻底测试过。

关键是,如果你按降序对卡片进行排序,并删除任何重复的值,那么只有几种可能的方法可以排列直线(你只需要检查末端):

  • 如果第一张和第五张牌相隔四张,它们代表最高的直线(因为我们知道它们之间的牌没有重复值)
  • 按顺序,第二张和第六张牌以及第三张和第七张牌也是如此(如果还有那么多唯一值的话)
  • 唯一的另一种可能性是,如果我们在排序列表的开头有一张王牌,在结尾有五到二张牌,代表a-5直落

这是代码:

public static IEnumerable<Card> GetBestStraight(IEnumerable<Card> sevenCards)
{
    if (sevenCards.Count() != 7)
    {
        throw new ArgumentException("Wrong number of cards", "sevenCards");
    }
    List<Card> ordered = sevenCards.OrderByDescending(c => c.Val).ToList();
    List<Card> orderedAndUnique = ordered.Where((c, i) => i == 0 || ordered[i].Val != ordered[i - 1].Val).ToList();
    if (orderedAndUnique.Count < 5)
    {
        // not enough distinct cards for a straight
        return Enumerable.Empty<Card>();
    }
    if (orderedAndUnique[0].Val == orderedAndUnique[4].Val + 4)
    {
        // first five cards are a straight
        return orderedAndUnique.Take(5);
    }
    else if (5 < orderedAndUnique.Count && orderedAndUnique[1].Val == orderedAndUnique[5].Val + 4)
    {
        // next five cards are a straight
        return orderedAndUnique.Skip(1).Take(5);
    }
    else if (6 < orderedAndUnique.Count && orderedAndUnique[2].Val == orderedAndUnique[6].Val + 4)
    {
        // last five cards are a straight
        return orderedAndUnique.Skip(2).Take(5);
    }
    // if there's an A-5 straight, the above won't have found it (because Ace and Two are not consecutive in the enum)
    if (orderedAndUnique[0].Val == CARDS.Ace && orderedAndUnique[orderedAndUnique.Count - 4].Val == CARDS.Five)
    {
        return orderedAndUnique.Where(c => c.Val == CARDS.Ace || c.Val <= CARDS.Five);
    }
    return Enumerable.Empty<Card>();
}

愚蠢和津贴一样!当最快的灵魂是一个简单的小面具:时,我为什么这么担心使用LINQ

    List<Card> cardList = new List<Card>();
    cardList.Add(new Card { Suit = SUITS.Diamonds, Val = RANK.Two });
    cardList.Add(new Card { Suit = SUITS.Hearts, Val = RANK.Three });
    cardList.Add(new Card { Suit = SUITS.Clubs, Val = RANK.Five });
    cardList.Add(new Card { Suit = SUITS.Diamonds, Val = RANK.Seven });
    cardList.Add(new Card { Suit = SUITS.Hearts, Val = RANK.Four });
    cardList.Add(new Card { Suit = SUITS.Clubs, Val = RANK.King });
    cardList.Add(new Card { Suit = SUITS.Diamonds, Val = RANK.Ace });
    int card = 0;
    foreach (Card c in cardList)
    {
        card |= 1 << (int)c.Val - 1;
    }
    bool isStraight = false;
    RANK high = RANK.Five;
    int mask = 0x1F;
    while (mask < card)
    {
        ++high;
        if ((mask & card) == mask)
        {
            isStraight = true;
        }
        else if (isStraight)
        {
            --high;
            break;
        }
        mask <<= 1;
    }
    // Check for Ace low
    if ((!isStraight) && ((0x100F & card) == 0x100F))
    {
        isStraight = true;
        high = RANK.Five;
    }
    return card;