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