Home > database >  Logical function of the sample numbers to be sequential
Logical function of the sample numbers to be sequential

Time:10-06

Suppose I am generating a matrix of card, each has its own letter (A/B/C) and its own number (2-11).

Now I randomly sampled 5 cards.

card <- data.frame(
 pack = rep(c("A","B","C"), 10),
 rank = rep(2:11, 3)
) 

card

   pack rank
1     A    2
2     B    3
3     C    4
4     A    5
5     B    6
6     C    7
7     A    8
8     B    9
9     C   10
10    A   11
11    B    2
12    C    3
13    A    4
14    B    5
15    C    6
16    A    7
17    B    8
18    C    9
19    A   10
20    B   11
21    C    2
22    A    3
23    B    4
24    C    5
25    A    6
26    B    7
27    C    8
28    A    9
29    B   10
30    C   11

s<-card[sample(seq_len(nrow(card)), 5),]
s

And then our sample is generated.

Now, define "sequential rank".

A sample is said to be containing five cards of sequential rank if the numbers are consecutive.

For example, the cards with rank 3 4 5 6 7 is sequential.

Also we define that 2 to be a special number, such that

2 3 4 5 6 is sequential. 8 9 10 11 2 is also sequential. But 9 10 11 2 3 is not sequential.

My target is to set a logical function such that the output is TRUE if the sample cards are sequential, FALSE otherwise.

Now I try to list all the sequential combination out

2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
5 6 7 8 9
6 7 8 9 10
7 8 9 10 11
8 9 10 11 2

But note that 2 4 3 5 6 is also sequential, and the like. So we have actually many many cases to deal with.

My question is, is there a more compact way to set up the aforementioned function (TRUE if the sample cards are sequential, FALSE otherwise.) ?

CodePudding user response:

Here's another approach:

is_run <- function(x) {
  
  x <- sort(x)
  
  # All consecutive runs should return TRUE
  if(all(diff(x) == 1)) return(TRUE)
  
  # The special case of 8, 9, 10, 11, 2 should also return TRUE
  if(all(x == c(2, 8, 9, 10, 11))) return(TRUE)
  
  # In all other cases this is not a consecutive run
  return(FALSE)
}

Tests:

# Sequence
is_run(c(4, 5, 6, 7, 8))
#> [1] TRUE

# Sequence starting with 2
is_run(c(2, 4, 3, 5, 6))
#> [1] TRUE

# Sequence ending with 2
is_run(c(8, 9, 10, 11, 2))
#> [1] TRUE

# Cards not in order but can be arranged into sequence
is_run(c(6, 5, 8, 7, 4))
#> [1] TRUE

# Cards not in order but can be arranged into sequence (including a 2)
is_run(c(9, 11, 10, 2, 8))
#> [1] TRUE

# Non-sequence 
is_run(c(3, 4, 6, 7, 8))
#> [1] FALSE

# Wrap around not allowed
is_run(c(10, 11, 2, 3, 4))
#> [1] FALSE

Created on 2022-10-06 with reprex v2.0.2

CodePudding user response:

Exclude special number 2, then sort and check if difference is always 1.

# example input
d <- read.table(text = "
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
5 6 7 8 9
6 7 8 9 10
7 8 9 10 11
8 9 10 11 2
9 10 11 2 3")

foo <- function(x) all(diff(sort(x[ x != 2 ])) == 1)

apply(d, 1, foo)
# [1]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE

CodePudding user response:

So you want a function that tells you if a vector of integers contains a straight sequence when ordered.

Edit: And if the last number is 2 but everything else is sequential, it should still be deemed sequential.

This should work:

is.sequential <- function(x) {
  if (x[length(x)]==2) {
    x <- x[-length(x)] # neglect last element if it is 2
    if (2 %in% x) return(FALSE) # but then no other 2s are allowed
  }
  length(x) == max(x) - min(x)   1
}

x <- c(1, 4, 5, 2, 3)
is.sequential(x) # TRUE

y <- c(1, 2, 3, 5, 6)
is.sequential(y) # FALSE

z <- c(4, 5, 6, 7, 2)
is.sequential(z) # TRUE

z2 <- c(2, 3, 4, 5, 2)
is.sequential(z2) # FALSE

Note that this function will not work well if x contains anything that is not an integer.

CodePudding user response:

It is unclear exactly how 2 operates. This solution assumes the only "sequential rank" multisets involving 2 are {2, 3, 4, 5, 6} and {2, 8, 9, 10, 11} (e.g., {2, 4, 5, 6, 7} and {2, 2, 9, 10, 11} are not considered "sequential rank").

!any((diff(sort(s)) - 1) %% 5:8)

It can also be vectorized for a 5-by-n matrix:

library(Rfast)
colAll(!((diff(colSort(s)) - 1) %% 5:8))

Example usage:

set.seed(1013119055L)
s <- matrix(sample(2:11, 5e3, TRUE), 5)
s[,colAll(!((diff(colSort(s)) - 1) %% 5:8))]
#>      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#> [1,]    3    7    4    6    9    3    9    8   11     6
#> [2,]    7    8    2    2    8    7    8    7    2     8
#> [3,]    4    9    6    4   10    5    7    6   10     7
#> [4,]    5   10    5    5   11    4    6   10    9    10
#> [5,]    6    6    3    3    7    6    5    9    8     9
  • Related