Praat combining two tiers into one tier - praat

I have two tiers within a textgrid and I'd like to combine them into one tier. Each tier is a different speaker. The intervals within each tier don't overlap. Is this possible? I've seen a lot of Praat scripts that will merge or concatenate but nothing to combine them. I've trying to use the Python tgt module to do this but it's been a slow go. I've been digging for a while so any suggestions on how to attack this problem are greatly appreciated! Thanks.

Some time ago I wrote a script that was used to find non-overlapping intervals, also in tiers with different speakers. Although the task is different, the TextGrid that script creates to work on can be used for what you want.
The logic of that script is in a procedure that can be included into whatever script you are using (or canibalised into it, if you're into that sort of thing).
If you include that procedure (either physically copying it or includeing it), you can do
old = selected("TextGrid")
#toNonOverlappingTiers()
new = selected("TextGrid")
After that, the new TextGrid will have a single tier will all the intervals from the other tiers "flattened out". Each interval in this new tier with a non-zero label will represent a chunk of the old TextGrid contained in labeled intervals in at most one tier. Unlabeled intervals were not contained in any labeled intervals in old. The remaining labels tell you the tier number of the only labeled interval that contained them.
So after running the example above, you can do
# Loop through intervals in the new TextGrid
selectObject: new
for i to do("Get number of intervals...", 1)
label$ = Get label of interval: 1, i
# Since your original TextGrid had no overlapping intervals
# you only want labeled intervals (there will be no zeros)
if label$ != ""
# Get the midpoint, to match to intervals in a different
# tier in the original TextGrid
tier = number(label$)
start = Get start point: 1, i
end = Get end point: 1, i
midpoint = ((end - start) / 2) + start
# Get the original label
selectObject: old
j = Get interval at time: tier, midpoint
original$ = Get label of interval: tier, j
# Apply the original label to the new TextGrid
selectObject: new
Set interval text: 1, i, original$
endif
endfor
For archival purposes:
# This procedure is a part of the tgutils plugin
# Please see http://cpran.net/plugins/tgutils for more details
procedure toNonOverlappingIntervals ()
# Original TextGrid
.tg = selected("TextGrid")
.tiers = Get number of tiers
.start = Get start time
.end = Get end time
# Overlap TextGrid
.id = Create TextGrid: .start, .end, "overlap", ""
# Populate overlap tier with "flattened" intervals from all tiers
for .tier to .tiers
selectObject: .tg
.intervals = Get number of intervals: .tier
for .interval to .intervals-1
selectObject: .tg
.end = Get end point: .tier, .interval
# We use nocheck because there might already be a boundary there
selectObject: .id
nocheck Insert boundary: 1, .end
endfor
endfor
# Cycle through the flattened intervals to check how many spoken intervals
# align with each. A segment in the overlap tier will be considered to have no
# overlap if and only if there is one tier with a speech labeled interval which
# coincides with it.
selectObject: .id
.flat_intervals = Get number of intervals: 1
for .interval to .flat_intervals
.start = Get start point: 1, .interval
.end = Get end point: 1, .interval
.midpoint = (.end - .start) / 2 + .start
# Count how many speakers are speaking over that flattened interval
.speakers = 0
for .tier to .tiers
selectObject: .tg
.interval_number = Get interval at time: .tier, .midpoint
.label$ = Get label of interval: .tier, .interval_number
if .label$ != ""
# Increment the number of speakers for each labeled coinciding interval
# on any tier. We also save the tier number of the (last) speaker, so we
# know where to look for measurements later.
.speakers += 1
.speaker_tier = .tier
endif
endfor
# Label the overlap intervals. Blank intervals are matched by no speakers in
# any tier. Intervals labeled "0" are matched by more than one speaker, in
# more than one tier. The rest contain the tier number of the single speaker
# speaking at that time.
selectObject: .id
if .speakers = 1
Set interval text: 1, .interval, string$(.speaker_tier)
elif .speakers > 1
Set interval text: 1, .interval, "0"
else
Set interval text: 1, .interval, ""
endif
endfor
endproc

Related

praat script get pitch list for a certain word

I am trying to write a script for Praat and am having difficulty doing it.
What I want is to have a result for a certain word in a sentence (and a sound),
ex.: As you're not using your car at the moment, can I borrow it?
I want a pitch list for the word "moment". If I select the word "moment" and select pitch listing under the menu of pitch then it gives me the time and f0 for every 0.01 second (open both sound and TextGrid).
I have been searching and trying to script this but haven't succeeded yet.
Could you help me with this?
I have modified the upper question.
sentence: As you're not using your car at the moment, can I borrow it?
(I have a mp3 file, text grid with 2 tiers for this sentence, tier 1 is the word and tier 2 is phone)
The following is my script. I want to have the f0 max and min for the last syllable part of the word "moment" but only succeeded in having the f0 max and min for the whole interval 10("moment" in tier 1).
I also have a phone tier for the word "moment" (in tier 2), which is the following:
phone tier for the word "moment" is M OW M AH N T
=> and I want [M AH N T] 's f0 max and f0 min excluding [M OW] which is the first syllable part.
the following is the script I have so far.
form Get F0 Min-Max
sentence Directory ./
word Base_file_name
comment The name of result file
text textfile F0_list.txt
endform
# Create a header row for the result file:
header$ = "Filename TextGridLabel startTime endTime minTime f0min maxTime f0max'newline$'"
fileappend "'textfile$'" 'header$'
#Read all files in a folder
Create Strings as file list... mp3list 'directory$'/'base_file_name$'*.mp3
Create Strings as file list... gridlist 'directory$'/'base_file_name$'*.TextGrid
n = Get number of strings
for i to n
clearinfo
#We first extract pitch tiers
select Strings mp3list
filename$ = Get string... i
Read from file... 'directory$'/'filename$'
soundname$ = selected$ ("Sound")
To Pitch... 0.01 75 600
output$ = "'soundname$'.Pitch"
# Write to binary file... 'output$'
# Read grid files and extract the selected intervals in them
select Strings gridlist
gridname$ = Get string... i
Read from file... 'directory$'/'gridname$'
int=Get number of intervals... 1
# Calculates F0 max, and F0 min (I need interval 10 to be analyzed for the word "moment" so have the 1 10 for the label)
select TextGrid 'soundname$'
label$ = Get label of interval... 1 10
if label$ <> ""
startTime = Get starting point... 1 10
endTime = Get end point... 1 10
select Pitch 'soundname$'
f0max = Get maximum... startTime endTime Hertz Parabolic
maxTime = Get time of maximum... startTime endTime Hertz Parabolic
f0min = Get minimum... startTime endTime Hertz Parabolic
minTime = Get time of minimum... startTime endTime Hertz Parabolic
resultline$ =
"'soundname$''tab$''label$''tab$''syllableTime''tab$''endTime''tab$''minTime''tab$''f0min''tab$''maxTime''tab$''f0max'"
fileappend "'textfile$'" 'resultline$'
endif
fileappend "'textfile$'" 'newline$'
endfor
# clean up
select all
Remove
Could you help me with this? thank you so much.
Could you add a new tier to separate out just the part you are interested in? Are there any other sentences in your data?
# define your variables
phoneTier = 2
newTier = 3
name$ = "syllable"
regexFirst$ = "M"
regexLast$ = "T"
# use this to add a new tier in each text grid
select TextGrid 'soundname$'
Insert interval tier: newTier, name$
# loop through the phones to find the start time of the syllable
# put this in the for loop that loops through each textgrid
phoneInt = Get number of intervals... phoneTier
for i from 1 to phoneInt-3
label$ = Get label of interval... i phoneTier
labelNext$ = Get label of interval... i+3 phoneTier
if index_regex(label$, regexFirst$) & index_regex(labelNext$, regexLast$)
start = Get starting point... i phoneTier
end = Get end point... i+3 phoneTier
Insert boundary... newTier start
Insert boundary... newTier end
syllableInterval = Get low interval at time: newTier, end
Set interval text... newTier syllableInterval "syllable"
endif
endfor
then, take the measurement of the intervals you just created.

How to compare alternating rows in CSV using RUBY

I have a data set that consists thousand of rows. I would like to count how many times an alarm toggle between ALARM_OPENED and ALARM_NORMALIZED
Here is a data sample. The Alarm toggle twice and hence ideally the count = 2
The issue now is I cannot figure how to
1) compare ALARM _OPENED and ALARM_NORMALIZED for the event type
2) To compare the difference in time between the change in event (the toggling should happen within a time frame of two seconds.)
count = 0
#loop this
if event_type[0] = 'ALARM_OPENED'
if event_type[1] = 'ALARM_NORMALIZED'
#time[0] - time[1] = 2 seconds
count = count + 1
end
end
p count
If you can assume that you always have a bunch of OPENED/NORMALIZED pairs, you can slice the array into pairs:
event_type.each_slice(2) do |opened, normalized|
break unless normalized # unpaired event at the end
# whatever you want to do with the two events here
end

Neural network - solve a net with time arrays and different sample rate

I have 3 measurements for a machine. Each measurement is trigged every time its value changes by a certain delta.
I have these 3 data sets, represented as Matlab objects: T1, T2 and O. Each of them has a obj.t containing the timestamp values and obj.y containing the measurement values.
I will measure T1 and T2 for a long time, but O only for a short period. The task is to reconstruct O_future from T1 and T2, using the existing values for O for training and validation.
Note that T1.t, T2.t and O.t are not equal, not even their frequency (I might call it 'variable sample rate', but not sure if this name applies).
Is it possible to solve this problem using Matlab or other software? Do I need to resample all data to a common time vector?
Concerning the common time. Below some basic code which does this. (I guess you might know how to do it but just in case). However, the second option might bring you further...
% creating test signals
t1 = 1:2:100;
t2 = 1:3:200;
to = [5 6 100 140];
s1 = round (unifrnd(0,1,size(t1)));
s2 = round (unifrnd(0,1,size(t2)));
o = ones(size(to));
maxt = max([t1 t2 to]);
mint = min([t1 t2 to]);
% determining minimum frequency
frequ = min([t1(2:length(t1)) - t1(1:length(t1)-1) t2(2:length(t2)) - t2(1:length(t2)-1) to(2:length(to)) - to(1:length(to)-1)] );
% create a time vector with highest resolution
tinterp = linspace(mint,maxt,(maxt-mint)/frequ+1);
s1_interp = zeros(size(tinterp));
s2_interp = zeros(size(tinterp));
o_interp = zeros(size(tinterp));
for i = 1: length(t1)
s1_interp(ceil(t1(i))==floor(tinterp)) =s1(i);
end
for i = 1: length(t2)
s2_interp(ceil(t2(i))==floor(tinterp)) =s2(i);
end
for i = 1: length(to)
o_interp(ceil(to(i))==floor(tinterp)) = o(i);
end
figure,
subplot 311
hold on, plot(t1,s1,'ro'), plot(tinterp,s1_interp,'k-')
legend('observation','interpolation')
title ('signal 1')
subplot 312
hold on, plot(t2,s2,'ro'), plot(tinterp,s2_interp,'k-')
legend('observation','interpolation')
title ('signal 2')
subplot 313
hold on, plot(to,o,'ro'), plot(tinterp,o_interp,'k-')
legend('observation','interpolation')
title ('O')
Its not ideal as for large vectors this might become ineffective as soon as you have small sampling frequencies in one of the signals which will determine the lowest resolution.
Another option would be to define a coarser time vector and look at the number of events that happend in a certain period which might have some predictive power as well (not sure about your setup).
The structure would be something like
coarse_t = 1:5:100;
s1_coarse = zeros(size(coarse_t));
s2_coarse = zeros(size(coarse_t));
o_coarse = zeros(size(coarse_t));
for i = 2:length(coarse_t)
s1_coarse(i) = sum(nonzeros(s1(t1<coarse_t(i) & t1>coarse_t(i-1))));
s2_coarse(i) = sum(nonzeros(s2(t2<coarse_t(i) & t2>coarse_t(i-1))));
o_coarse(i) = sum(nonzeros(o(to<coarse_t(i) & to>coarse_t(i-1))));
end

How can one analyze the greatest percentage gain (burst) of numbers in sequence in an array?

There are algorithms for detecting the maximum subarray within an array (both contiguous and non-continguous). Most of them are based around having both negative and positive numbers, though. How is it done with positive numbers only?
I have an array of values of a stock over a consequtive range of time (let's say, the array contains values for all consecutive months).
[15.42, 16.42, 17.36, 16.22, 14.72, 13.95, 14.73, 13.76, 12.88, 13.51, 12.67, 11.11, 10.04, 10.38, 10.14, 7.72, 7.46, 9.41, 11.39, 9.7, 12.67, 18.42, 18.44, 18.03, 17.48, 19.6, 19.57, 18.48, 17.36, 18.03, 18.1, 19.07, 21.02, 20.77, 19.92, 18.71, 20.29, 22.36, 22.38, 22.39, 22.94, 23.5, 21.66, 22.06, 21.07, 19.86, 19.49, 18.79, 18.16, 17.24, 17.74, 18.41, 17.56, 17.24, 16.04, 16.05, 15.4, 15.77, 15.68, 16.29, 15.23, 14.51, 14.05, 13.28, 13.49, 13.12, 14.33, 13.67, 13.13, 12.45, 12.48, 11.58, 11.52, 11.2, 10.46, 12.24, 11.62, 11.43, 10.96, 10.63, 10.19, 10.03, 9.7, 9.64, 9.16, 8.96, 8.49, 8.16, 8.0, 7.86, 8.08, 8.02, 7.67, 8.07, 8.37, 8.35, 8.82, 8.58, 8.47, 8.42, 7.92, 7.77, 7.79, 7.6, 7.18, 7.44, 7.74, 7.47, 7.63, 7.21, 7.06, 6.9, 6.84, 6.96, 6.93, 6.49, 6.38, 6.69, 6.49, 6.76]
I need an algorithm to determine for each element the single time period where it had the biggest percentage gain. This could be a time period of 1 month, some span of several months, or the entire array (e.g., 120 months), depending on the stock. I then want to output the burst, in terms of percentage gain, as well as the return (change in price over the original price; so the peak price vs the starting price in the period).
I've combined the max subarray type algorithms, but realized that this problem is a bit different; the array has no negative numbers, so those algorithms just report the entire array as the period and the sum of all elements as the gain.
The algorithms I mentioned are located here and here, with the latter being based on the Master Theorem. Hope this helps.
I'm coding in Ruby but pseudocode would be welcome, too.
I think you went the wrong way ...
I'm not familiar with ruby but let us build the algorithm in pseudocode using your own words :
I've got an array that contains the values of a stock over a range of
time (let's say, for this example, each element is the value of the
stock in a month; the array contains values for all consecutive
months).
We'll name this array StockValues, its length is given by length(StockValues), assume it is 1 based (first item is retrieved with StockValues[1])
I need an algorithm to analyze the array, and determine for each
element the single time period where it had the biggest percentage
gain in price.
You want to know for a given index i at which index j with j>i we have a maximum gain in percent i.e. when gain=100*StockValues[j]/StockValues[i]-100 is maximum.
I then want to output the burst, in terms of percentage gain, as well
as the return(change in price over the original price; so the peak price
vs the starting price in the period).
You want to retrieve the two values burst=gain=100*StockValues[j]/StockValues[i]-100 and return=StockValues[j]-StickValues[i]
The first step will be to loop thru the array and for each element do a second loop to find when the gain is maximum, when we find a maximum we save the values you want in another array named Result (let us assume this array is initialized with invalid values, like burst=-1 which means no gain over any period can be found)
for i=1 to length(StockValues)-1 do
max_gain=0
for j=i+1 to length(StockValues) do
gain=100*StockValues[j]/StockValues[i]-100
if gain>max_gain then
gain=max_gain
Result[i].burst=gain
Result[i].return=StockValues[j]-StockValues[i]
Result[i].start=i
Result[i].end=j
Result[i].period_length=j-i+1
Result[i].start_price=StockValues[i]
Result[i].end_price=StockValues[j]
end if
end for
end for
Note that this algorithm gives the smallest period, if you replace gain>max_gain with gain>=max_gain you'll get the longest period in the case there are more than one period with the same gain value. Only positive or null gains are listed, if there is no gain at all, Result will contain the invalid value. Only period>1 are listed, if period of 1 are accepted then the worst gain possible would be 0%, and you would have to modify the loops i goes to length(StockValues) and j starts at i
This doesn't really sound like several days of work :p unless I'm missing something.
# returns array of percentage gain per period
def percentage_gain(array)
initial = array[0]
after = 0
percentage_gain = []
1.upto(array.size-1).each do |i|
after = array[i]
percentage_gain << (after - initial)/initial*100
initial = after
end
percentage_gain
end
# returns array of amount gain $ per period
def amount_gain(array)
initial = array[0]
after = 0
amount_gain = []
1.upto(array.size-1).each do |i|
after = array[i]
percentage_gain << (after - initial)
initial = after
end
amount_gain
end
# returns the maximum amount gain found in the array
def max_amount_gain(array)
amount_gain(array).max
end
# returns the maximum percentage gain found in the array
def max_percentage_gain(array)
percentage_gain(array).max
end
# returns the maximum potential gain you could've made by shortselling constantly.
# i am basically adding up the amount gained when you would've hit profit.
# on days the stock loses value, i don't add them.
def max_potential_amount_gain(array)
initial = array[0]
after = 0
max_potential_gain = 0
1.upto(array.size-1).each do |i|
after = array[i]
if after - initial > 0
max_potential_gain += after - initial
end
initial = after
end
amount_gain
end
array = [15.42, 16.42, 17.36, 16.22, 14.72, 13.95, 14.73, 13.76, 12.88, 13.51, 12.67, 11.11, 10.04, 10.38, 10.14, 7.72, 7.46, 9.41, 11.39, 9.7, 12.67, 18.42, 18.44, 18.03, 17.48, 19.6, 19.57, 18.48, 17.36, 18.03, 18.1, 19.07, 21.02, 20.77, 19.92, 18.71, 20.29, 22.36, 22.38, 22.39, 22.94, 23.5, 21.66, 22.06, 21.07, 19.86, 19.49, 18.79, 18.16, 17.24, 17.74, 18.41, 17.56, 17.24, 16.04, 16.05, 15.4, 15.77, 15.68, 16.29, 15.23, 14.51, 14.05, 13.28, 13.49, 13.12, 14.33, 13.67, 13.13, 12.45, 12.48, 11.58, 11.52, 11.2, 10.46, 12.24, 11.62, 11.43, 10.96, 10.63, 10.19, 10.03, 9.7, 9.64, 9.16, 8.96, 8.49, 8.16, 8.0, 7.86, 8.08, 8.02, 7.67, 8.07, 8.37, 8.35, 8.82, 8.58, 8.47, 8.42, 7.92, 7.77, 7.79, 7.6, 7.18, 7.44, 7.74, 7.47, 7.63, 7.21, 7.06, 6.9, 6.84, 6.96, 6.93, 6.49, 6.38, 6.69, 6.49, 6.76]

Formula for calculating Exotic wagers such as Trifecta and Superfecta

I am trying to create an application that will calculate the cost of exotic parimutuel wager costs. I have found several for certain types of bets but never one that solves all the scenarios for a single bet type. If I could find an algorithm that could calculate all the possible combinations I could use that formula to solve my other problems.
Additional information:
I need to calculate the permutations of groups of numbers. For instance;
Group 1 = 1,2,3
Group 2 = 2,3,4
Group 3 = 3,4,5
What are all the possible permutation for these 3 groups of numbers taking 1 number from each group per permutation. No repeats per permutation, meaning a number can not appear in more that 1 position. So 2,4,3 is valid but 2,4,4 is not valid.
Thanks for all the help.
Like most interesting problems, your question has several solutions. The algorithm that I wrote (below) is the simplest thing that came to mind.
I found it easiest to think of the problem like a tree-search: The first group, the root, has a child for each number it contains, where each child is the second group. The second group has a third-group child for each number it contains, the third group has a fourth-group child for each number it contains, etc. All you have to do is find all valid paths from the root to leaves.
However, for many groups with lots of numbers this approach will prove to be slow without any heuristics. One thing you could do is sort the list of groups by group-size, smallest group first. That would be a fail-fast approach that would, in general, discover that a permutation isn't valid sooner than later. Look-ahead, arc-consistency, and backtracking are other things you might want to think about. [Sorry, I can only include one link because it's my first post, but you can find these things on Wikipedia.]
## Algorithm written in Python ##
## CodePad.org has a Python interpreter
Group1 = [1,2,3] ## Within itself, each group must be composed of unique numbers
Group2 = [2,3,4]
Group3 = [3,4,5]
Groups = [Group1,Group2,Group3] ## Must contain at least one Group
Permutations = [] ## List of valid permutations
def getPermutations(group, permSoFar, nextGroupIndex):
for num in group:
nextPermSoFar = list(permSoFar) ## Make a copy of the permSoFar list
## Only proceed if num isn't a repeat in nextPermSoFar
if nextPermSoFar.count(num) == 0:
nextPermSoFar.append(num) ## Add num to this copy of nextPermSoFar
if nextGroupIndex != len(Groups): ## Call next group if there is one...
getPermutations(Groups[nextGroupIndex], nextPermSoFar, nextGroupIndex + 1)
else: ## ...or add the valid permutation to the list of permutations
Permutations.append(nextPermSoFar)
## Call getPermutations with:
## * the first group from the list of Groups
## * an empty list
## * the index of the second group
getPermutations(Groups[0], [], 1)
## print results of getPermutations
print 'There are', len(Permutations), 'valid permutations:'
print Permutations
This is the simplest general formula I know for trifectas.
A=the number of selections you have for first; B=number of selections for second; C=number of selections for third; AB=number of selections you have in both first and second; AC=no. for both first and third; BC=no. for both 2nd and 3rd; and ABC=the no. of selections for all of 1st,2nd, and third.
the formula is
(AxBxC)-(ABxC)-(ACxB)-(BCxA)+(2xABC)
So, for your example ::
Group 1 = 1,2,3
Group 2 = 2,3,4
Group 3 = 3,4,5
the solution is:: (3x3x3)-(2x3)-(1x3)-(2x3)+(2x1)=14. Hope that helps
There might be an easier method that I am not aware of. Now does anyone know a general formula for First4?
Revised after a few years:-
I re logged into my SE account after a while and noticed this question, and realised what I'd written didn't even answer you:-
Here is some python code
import itertools
def explode(value, unique):
legs = [ leg.split(',') for leg in value.split('/') ]
if unique:
return [ tuple(ea) for ea in itertools.product(*legs) if len(ea) == len(set(ea)) ]
else:
return [ tuple(ea) for ea in itertools.product(*legs) ]
calling explode works on the basis that each leg is separated by a /, and each position by a ,
for your trifecta calculation you can work it out by the following:-
result = explode('1,2,3/2,3,4/3,4,5', True)
stake = 2.0
cost = stake * len(result)
print cost
for a superfecta
result = explode('1,2,3/2,4,5/1,3,6,9/2,3,7,9', True)
stake = 2.0
cost = stake * len(result)
print cost
for a pick4 (Set Unique to False)
result = explode('1,2,3/2,4,5/3,9/2,3,4', False)
stake = 2.0
cost = stake * len(result)
print cost
Hope that helps
AS a punter I can tell you there is a much simpler way:
For a trifecta, you need 3 combinations. Say there are 8 runners, the total number of possible permutations is 8 (total runners)* 7 (remaining runners after the winner omitted)* 6 (remaining runners after the winner and 2nd omitted) = 336
For an exacta (with 8 runners) 8 * 7 = 56
Quinellas are an exception, as you only need to take each bet once as 1/2 pays as well as 2/1 so the answer is 8*7/2 = 28
Simple
The answer supplied by luskin is correct for trifectas. He posed another question I needed to solve regarding First4. I looked everywhere but could not find a formula. I did however find a simple way to determine the number of unique permutations, using nested loops to exclude repeated sequences.
Public Function fnFirst4PermCount(arFirst, arSecond, arThird, arFourth) As Integer
Dim intCountFirst As Integer
Dim intCountSecond As Integer
Dim intCountThird As Integer
Dim intCountFourth As Integer
Dim intBetCount As Integer
'Dim arFirst(3) As Integer
'Dim arSecond(3) As Integer
'Dim arThird(3) As Integer
'Dim arFourth(3) As Integer
'arFirst(0) = 1
'arFirst(1) = 2
'arFirst(2) = 3
'arFirst(3) = 4
'
'arSecond(0) = 1
'arSecond(1) = 2
'arSecond(2) = 3
'arSecond(3) = 4
'
'arThird(0) = 1
'arThird(1) = 2
'arThird(2) = 3
'arThird(3) = 4
'
'arFourth(0) = 1
'arFourth(1) = 2
'arFourth(2) = 3
'arFourth(3) = 4
intBetCount = 0
For intCountFirst = 0 To UBound(arFirst)
For intCountSecond = 0 To UBound(arSecond)
For intCountThird = 0 To UBound(arThird)
For intCountFourth = 0 To UBound(arFourth)
If (arFirst(intCountFirst) <> arSecond(intCountSecond)) And (arFirst(intCountFirst) <> arThird(intCountThird)) And (arFirst(intCountFirst) <> arFourth(intCountFourth)) Then
If (arSecond(intCountSecond) <> arThird(intCountThird)) And (arSecond(intCountSecond) <> arFourth(intCountFourth)) Then
If (arThird(intCountThird) <> arFourth(intCountFourth)) Then
' Debug.Print "First " & arFirst(intCountFirst), " Second " & arSecond(intCountSecond), "Third " & arThird(intCountThird), " Fourth " & arFourth(intCountFourth)
intBetCount = intBetCount + 1
End If
End If
End If
Next intCountFourth
Next intCountThird
Next intCountSecond
Next intCountFirst
fnFirst4PermCount = intBetCount
End Function
this function takes four string arrays for each position. I left in test code (commented out) so you can see how it works for 1/2/3/4 for each of the four positions

Resources