System and method for optimizing medical diagnosis, procedures and claims using a structured search space6393404Abstract A system and method for optimizing medical diagnosis, procedures and reimbursement claims using a structured search space is described. The system may comprise: a master procedure list of alphanumeric codes that represent each of a plurality of medical procedures, wherein the master procedure list includes simple procedures and compound procedures which consist of at least two simple procedures; a value associated with each of the plurality of medical procedures; a list of ordered procedures listing medical procedures for a specific medical encounter; a search tree of all possible combinations of the simple procedures and the compound procedures in the list of ordered procedures; and a minimum total of the values associated with the medical procedures in the list of ordered procedures. Claims What is claimed is: Description FIELD OF THE INVENTION
TABLE 1
Codes
1008
C001
Table 1 shows an example of two procedure codes, 1008 and C001. 1008 is a simple procedure and C001 is a compound procedure. Procedures can be simple procedures or compound procedures that consist of more than one simple procedure (compound procedures will be interchangeably used with panels throughout this description). Master Procedure List The master procedure list is made up of reference data records and will be explained with reference to table 2 below.
TABLE 2
Codes Short Description Long Description Reimb. Fees
1004 short desc. 1 long desc. 1 $2.00
1005 short desc. 2 long desc. 2 $1.00
1006 short desc. 3 long desc. 3 $4.00
1008 short desc. 4 long desc. 4 $5.00
C001 short desc. 5 long desc. 5 $3.00
(1004, 1005)
C002 short desc. 6 long desc. 6 $4.00
(1004, 1006)
C003 short desc. 7 long desc. 7 $5.50
(1005, 1008)
The master procedure list has a reference data record for all possible procedures with their associated identification codes. Each reference data record also includes a short description, a long description, and associated reimbursement fees. The long description also contains a listing of all of the simple procedures if that record is for a compound procedure. The master procedure list and its required data structures are used during the process to provide the fees, and the components of compound procedures, for determining new possible combinations. Output Data A set of procedure codes that are derived from the ordered procedures by using different combinations of the compound and simple procedure codes that represent the minimal reimbursement possible. This output data is hereafter referred to as billable procedures. The process is first presented with a list of procedure codes to be optimized. This list of procedures can contain duplicates (the same procedure ordered more than once) and any combination of simple procedures and/or panels. The optimization process will be described in three phases. Phase I is the preparation phase whereby the input data is processed to remove compound procedures or duplicate procedures. Phase II is the construction of the search space. Phase III is the traversal of the search space to locate the optimum billing structure. Phase I--Preparation of Input Data The ordered procedures are checked to see if any of the procedures are panels by comparing each ordered procedure with the master procedure list and then checked for duplicate procedures. It is important to note that the duplicates check is to determine whether a simple procedure is ordered more than once which requires that any compound procedures or panels be unbundled (disassembled) prior to removal of duplicates. For each entry in the ordered procedures list a search is performed to determine whether the entry is a panel as indicated by the presence of a list of component procedures in the master procedure list for the ordered procedure code in question. If an ordered procedure is determined to be a panel then the procedure code is removed from the ordered procedure list and the components of the panel are added to the ordered procedure list. This process is performed recursively for each procedure in the ordered procedures list until all procedures in the ordered procedures list are simple (non-panel) procedures. The unbundling process will require either a recursive implementation or a multi-pass implementation if a panel or compound procedure exists that has another compound procedure as a component. After unbundling all compound procedures the list is processed again to remove any duplicate procedure codes. Beginning with the first entry in the list each entry is compared to all remaining entries in the list looking for duplicates. When a duplicate is found, it is removed from the list and the process continues until no more duplicates remain. Phase II--Construction of the Search Space The construction of the search space requires inputting the set of ordered procedures with all compound procedures unbundled and all duplicate procedures removed, as described in Phase I. The search space construction involves building a set of data structures that represent all possible combinations of compound and simple procedures. For example, a set of simple procedure codes would be billable as Panel 42 (note: these panel names do not represent real panels as used by HCFA and are for the purpose of illustration only) and Panel 28 with a few simple procedures remaining. Alternately, they would be billable as Panel 38 with no other Panels or simple procedures. Finally, they would be billable as a collection of simple procedure codes using no panels. The search space must represent all possible combinations of codes using compound procedures and simple procedures. After unbundling the compound procedures and removing the duplicates, the ordered procedures list is then used to start a methodical combination of the processed procedure list into various compound/simple procedure combinations. Prior to processing, a list of all compound procedures is made. Then, the first step of the recursive process is to examine the ordered procedure list to find a match between the components of the compound procedures and the ordered procedures. A matched compound procedure is produced when the components of that procedure match two or more of the ordered procedures. The component procedures of this newly matched compound procedure are then removed from the ordered procedures. The matched compound procedure code is then added to the ordered procedure list. The matched compound procedure is then removed from the selection list and the process is repeated until all compound procedures have been either used or discarded as a non-match. For each combination that has been started in this manner, the remaining simple procedures are now searched again to determine if there are any more compound procedure combinations that can be made. If so, the component procedures are then removed, the compound procedure code added and the remaining procedures are again searched until no more compound matches are found. This process is repeated until all combinations of compound procedures and simple procedures are found. The pseudocode below and the software code in Appendix A further illustrate how the search space can be constructed. In addition, the following example illustrates construction of this search space. Master Reference List (also Depicted in Table 2) 1004 1005 1006 1008 C001 (PANEL including 1004, 1005) C002 (PANEL including 1004, 1006) C003 (PANEL including 1005, 1008) Ordered Procedures (before Phase I--also Depicted in Table 1) C001 and 1008 Ordered Procedures (after Phase I) 1004, 1005, 1008 Phase II (Depicted in FIG. 6) 1.0--1004, 1005, 1008 (shown as node 82) 2.0--C001* (node 84) 2.1--1008 (node 86) 2.2--C003 (node 88) 3.0--C002* (node 90) 3.1--1005, 1008 (node 92) 3.2--C001* (node 94) 3.2.1--1008 (node 96) 3.2.2--C003 (node 98) 3.3--C003* (node 100) 3.3.1--1005 (node 102) 3.3.2--C001 (node 104) 4.0--C003* (node 106) 4.1--1004 (node 108) 4.2--C001 (node 110) The branches of the outline marked with asterisks are incomplete and have sub-branches that are needed to complete that combination chain by selecting one of the sub-components of that branch of the outline. For example, entry 1.0 is complete and corresponds to the input list. Branch 2.0 starts with the compound procedure C001 and can be completed by selecting either 2.1 or 2.2 making either [C001, 1008] or [C001, C003] a complete combination. The following list represents all possible combinations using these codes: [1004, 1005, 1008] [C001, 1008] [C001, C003] [C002, 1005, 1008] [C002, C001, 1008] [C002, C001, C003] [C002, C003, 1005] [C002, C003, C001] [C003, 1004] [C003, C001] Phase III--Traversal of the Search Space Using Desired Optimization Criteria The traversal phase requires a set criteria for evaluating the combination that is most appropriate for being identified in the search space. The primary criteria that is being used for this embodiment is minimum reimbursement with secondary emphasis placed on the least number of procedure codes in the combination. In the case where two procedure combinations have different reimbursement rates, the procedure with the lesser of the two rates will be selected. In the case where the reimbursement rates are the same, either the combination using the least number of procedure codes or the combination of the maximum compound procedure codes with the minimum simple procedure codes would be selected. Performing this traversal requires setting a global minimum fee indicating the lowest total in fees for a procedure combination. While traversing the search space, the fees for each combination are summed to obtain the total fee for that combination. This fee is then compared with the current global minimum fee. If the current fee is less than the global minimum, then the global minimum is set to the current fee and the current combination is saved as the current minimum combination. Each combination is processed exhaustively in this manner until all combinations have been reviewed. The summing process requires the fee for each component of the combination to be found and added to the running total, thus building the total fee for combination. During the fee building process the running total is described to be the partial fee. When all components of the combination have been found and added the partial fee becomes the total fee for the combination. The process can be optimized to reject combinations by calculating and comparing the partial fee with the current global minimum. If the partial fee exceeds the current global minimum, then the entire combination is rejected. This eliminates the need for further checking of the search space below the partial combination and thereby reduces any unnecessary review of combinations that would exceed the global minimum. FIG. 5 shows the search tree depicted in FIG. 4 with the minimum branch outlined as the combination of compound procedure C003 (node 106) and simple procedure 1004 (node 108). This combination includes all of the original procedures in the procedure list (1008 and C001--shown in FIG. 3) and has the minimum total cost ($7.50) for all of the procedures in the original procedure list. An Example of Pseudocode for Preferred Embodiment A pre-built incremental search tree is now described for illustration purposes. This implementation does not include search optimization techniques in order to clarify the technique. This example also uses the tree structure illustrated in FIG. 3 to create a search tree. GetChildCount(NODE)--returns number of children or 0 if none exists. InsertChildNode(NODE, CHILD NODE)--inserts a reference to CHILD NODE into NODE Count(LIST)--returns number of entries in list NewNode(LIST)--creates a new NODE and associates the LIST with the NODE. Note that the list and the CHILD NODES are not the same thing. The LIST parameter is stored with value semantics with the NODE while the CHILD NODES are completely separate NODES that have their own values stored in the form of LISTs. Procedures(PANEL)--Returns a LIST that contains the unbundled procedure codes for the specified panel. Procedures(NODE)--Same as Procedures (PANEL) except it returns the LIST of procedures that is associated with the NODE. PanelCode(PANEL)--Returns a LIST that contains 1 entry corresponding to the PANEL code. Note this single entry is returned as a LIST for the purpose of symmetry only. Since each NODE of the search space stores a list of codes, it makes the algorithm cleaner if PANEL codes are stored that way as well. GetChild(P,I)--Returns a NODE that corresponds to the Ith child NODE to NODE P. CalcValue(NODE)--Returns a value that corresponds to the sum of the regional Medicare reimbursement values for each procedure in the list returned by calling Procedures (NODE). Intersection(LIST,LIST)--Returns a LIST that is the intersection of the LIST structures passed for arguments. Subtraction(LIST1,LIST2)--Returns a LIST that contains all of the elements in LIST1 that are not also in LIST2.
HasPartialMatch
FUNCTION UnbundleCodes(ProcList)
NPROCS = Count(ProcList)
NPANELS = Count(PanelList)
PanelProcList = NULLPROCLIST
FOR I = 1 TO NPROCS
FOR J = 1 to NPANELS
If ProcList(I) = PanelCode(PanelList(J)) THEN
ProcList(I) = Procedures(PanelList(J))
BREAK
END IF
END FOR
END FOR
RETURN ProcList
END FUNCTION
ROUTINE BuildSearchTree(P,ProcList)
Q = NEWNODE(ProcList)
InsertChildNode(P,Q)
N = Count(PanelList)
FOR I = 1 to N
IF HasPartialMatch(ProcList,PanelList(I)) THEN
PanelProcList = ProcList & Procedures(PanelList(I))
Q = NEWNODE(PanelCode(PanelList(I)))
InsertChildNode(P,Q)
RemainingProcList = ProcList & (.about.PanelProcList)
BuildSearchTree(Q,RemainingProcList)
END IF
END FOR
END ROUTINE
FUNCTION GetLocalMinimum(P)
MinVal = +INFINITY
N = GetChildCount(P)
FOR I = 1 to N
Q = GetChild(P,I)
IF GetChildCount(Q) >0 THEN
Val = GetLocalMinimum(Q)
ELSE
Val = 0
Val = Val + CalcValue(Q)
IF MinVal > Val THEN
MinVal = Val
END FOR
RETURN MinVal
END FUNCTION
Root = NEWNODE(null)
UnbundledProcList = UnbundleCodes(ProcOrderList)
BuildSearchTree(Root,UnbundledProcList)
Reimbursement = GetLocalMinimum(Root)
A more detailed implementation is also included as Appendix A to demonstrate how the preferred embodiment would be implemented in the "C" computing language. The present invention has been described in connection with the preferred embodiment as described herein. Although an embodiment has been shown and described in detail herein, along with the certain variants thereof, many other varied embodiments that incorporate the teachings of the invention may be easily constructed by those skilled in the art. Accordingly, the present invention is not intended to be limited to specific form set forth herein, but on the contrary, it is intended to cover such alternatives, modifications, and equivalents, as can be reasonably included within the spirit and scope of the invention as defined by the following claims. APPENDIX A
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
#include <bitvector.h>
#include <list.h>
struct Procedure : public Object {
char *code;
char *desc;
double fee;
int isPanel; //indicates whether procedure is a panel
BitVector panelProcs; //if is panel, this contains the panel codes
};
struct TreeNode : public Object {
BitVector procedures; //list of procedures for this branch
List branches;
};
int numProcedures = 0;
Procedure *procedures = NULL ; // array of procedures
TreeNode rootNode;
int findCode(char *code)
{
for(int i=0;i<numProcedures;i++)
if( strcmpi(procedures[i].code,code)==0) return i;
return -1;
}
void trimString(char *str)
{
char *p = str;
while(isspace(*p)) p++;
int len = strlen(p);
memcpy(str,p,len);
str[len] = 0;
len = strlen(str);
if( len) {
p = str+(strlen(str)-1);
while(p>=str &&isspace(*p)) *p-- = 0;
}
}
void loadPanelDefinitions(char *filename)
{
FILE *fp = fopen(filename,"r");
assert(fp);
char buf[1024];
//load the panel definitions
while(fgets(buf,sizeof(buf),fp)) {
trimString(buf);
// get code and procedure
char *p = strtok(buf,"," );
int id = findCode(p);
if( id >= 0) {
Procedure *panel = &procedures[id];
panel->isPanel = TRUE;
while(p=strtok(NULL,"," )) {
int pid = findCode(p);
if( pid >= 0)
panel- >panelProcs.set(pid);
}
}
}
fclose(fp);
}
void buildProcedureList(char *filename)
{
FILE *fp = fopen(filename,"r");
assert(fp);
char buf[1024];
// get the first line containing the number of procedures
fgets(buf,sizeof(buf),fp);
numProcedures = atoi(buf);
assert(numProcedures>0&&numProcedures <20000); // the 20000 cap is
arbitrary just for sanity's sake
// allocate the array of codes
procedures = new Procedure[numProcedures];
assert(procedures)
//load the procedures;
int i = 0;
while(fgets(buf,sizeof(buf),fp)) {
trimString(buf);
// get code
char *p = strtok(buf,", " );
procedures[i].code = strdup(p);
procedures[i].isPanel = FALSE;
// get fee
p = strtok(NULL,"," );
procedures[i].fee = atof(p);
// get desc
p = strtok(NULL,"" );
if(p) {
while(*p&&(isspace(*p).vertline. .vertline.*p ==',')) p++;
procedures[i].desc = strdup(p);
}else
procedures[i].desc = strdup ("" );
//printf("%s,%f,%s.backslash.n" ,procedures[i].code,procedures[i].fee,pro-
cedures[i].des
c);
i++;
}
fclose(fp);
}
BitVector *loadOrders(char *filename)
{
BitVector *orders = new BitVector;
assert(orders);
FILE *fp =fopen(filename,"r");
assert(fp);
char buf[1024];
//load the panel definitions
while (fgets(buf,sizeof(buf),fp)) {
trimString(buf);
// get code and procedure
char *p = strtok(buf,"," );
int id = findCode(p);
assert(id>=0); //Invalid procedure code in ordered procedures
if( id >=0 ) orders-> set(id);
}
fclose(fp);
return orders;
}
void unbundlePanels(BitVector *procList)
{
// this routine makes multiple passes through orders list in case
// there is an occurance of a panel within a panel
int changed;
do{
changed = FALSE;
for(int i=procList->start();i<=procList->end() ;i++) {
if( procList->get(i) &&
procedures[i].isPanel) {
procList->clear(i);
procList->or(&procedures[i].panelProcs);
changed = TRUE;
}
}
} while(changed);
}
void dumpProcedureList(BitVector *procList)
{
int numPanels = 0;
int numProcs = 0;
double totalFee = 0.0;
// dump ordered procedures
for(int i=procList- > start();i<=procList->end();i ++) {
if( procList->get(i)) {
numProcs++;
totalFee += procedures[i].fee;
if( procedures[i].isPanel) numPanels++;
printf("%s - %s %s.backslash.n" ,
procedures[i].code,procedures[i].desc, (char
*)((procedures[i].isPanel)?"(PANEL)" :"" ));
}
}
printf(".backslash.n" );
printf("Total panels: %d.backslash.n" , numPanels);
printf("Total procedures: %d.backslash.n" , numProcs);
printf("Total Fee for Listed Procedures: $%-5.2f.backslash.n" , totalFee);
}
int panelProceduresMatch(Procedure *panelProc,BitVector *procList)
{
// first check to see if panel proc is special chemistry procedure
BitVector bv;
bv.or(&panelProc->panelProcs);
bv.and(procList);
//printf("* * * *Dumping merged procedure list* * *
*.backslash.n.backslash.n" );
//printf("Panel Procs.backslash.n" );
//dumpProcedureList(&panelProc->panelProcs);
//printf("procList.backslash.n" );
//dumpProcedureList(procList);
//printf("Merged.backslash.n" );
//dumpProcedureList(&bv);
//printf("**** END OF DUMP ****.backslash.n.backslash.n" );
for(int i=bv.start();i<=bv.end();i++) {
if( bv.get(i)) {
printf(" Matched %s on procedure
%s.backslash.n" ,panelProc->code,procedures[i].code);
return TRUE;
}
}
return FALSE;
}
void dumpTree(TreeNode *node,int lvl)
{
TreeNode *pNode = (TreeNode *)node->branches.First();
int cnt = 0;
while(pNode) {
printf("%*.*s%d:%d:" ,lvl,lvl,"
" ,lvl,cnt);
for(int
i=pNode->procedures.start();i<=pNode->procedures.end();i++) {
if( pNode->procedures.get(i))
printf("%s," ,procedures[i].code);
}
printf("*.backslash.n" );
dumpTree(pNode,lvl+1);
cnt ++;
pNode = (TreeNode *)pNode->next;
}
}
void buildTree(TreeNode *node,BitVector *procList)
{
TreeNode *pNode = new TreeNode;
assert(pNode);
pNode->procedures.or(procList);
node->branches.Insert(pNode);
for(int i=0;i<numProcedures;i++) {
if( procedures[i].isPanel &&
panelProceduresMatch(&procedures[i],procList)) {
// printf("Matched %s.backslash.n" ,procedures[i].desc);
BitVector panelProcList(numProcedures,TRUE);
panelProcList.clearAll();
panelProcList.or(&procedures[i].panelProcs);
panelProcList.and(procList);
pNode = new TreeNode;
assert(pNode);
pNode->procedures.set(i); // set the panel ID
node->branches.Insert(pNode);
BitVector remainingProcList(numProcedures,TRUE);
remainingProcList.or(procList);
for(int j=panelProcList.start();j<=panelProcList.end();j++)
if( panelProcList.get(j)) remainingProcList.clear(j);
if( !remainingProcList.isNull())
buildTree(pNode,&remainingProcList);
}
}
}
double calcFee(BitVector *procList)
{
double totalFee = 0.0;
// dump ordered procedures
for(int i=procList->start();i<=procList->end();i++)
if( procList->get(i))
totalFee +=procedures[i].fee;
return totalFee;
}
double getMinimumFee(TreeNode *node,BitVector *minProcs)
{
TreeNode *pNode = (TreeNode *)node->branches.First();
TreeNode *minNode = NULL;
double fee = 0.0;
BitVector minVector(numProcedures,TRUE);
while(pNode) {
double val = calcFee(&pNode->procedures);
BitVector bv(numProcedures,TRUE);
bv.clearAll();
bv.or(&pNode->procedures);
val = val + getMinimumFee(pNode,&bv);
if( !minNode .vertline. .vertline. val < fee) {
minNode = pNode;
fee = val;
minVector.clearAll();
minVector.or(&bv);
}
pNode = (TreeNode *)pNode->next;
}
minProcs-> or(&minVector);
return fee;
}
int main(int argc, char *argv[])
{
printf("Dynamedix Medicare Billing Optimization Patent Al-
gorithm.backslash.nUsing
Dallas Locale and Carrier Code For Fee Structure" );
buildProcedureList("codes.txt" );
loadPanelDefinitions("panels.txt" );
//load the orders, build the tree and report the results
BitVector *orders = loadOrders("orders.txt" );
// Unbundle and dump
printf(".backslash.nOriginal Order.backslash.n.backslash.n" );
dumpProcedureList(orders);
unbundlePanels(orders);
printf(".backslash.nOriginal Order Unbundled with Duplicates
Removed.backslash.n.backslash.n" );
dumpProcedureList(orders);
printf(".backslash.n.backslash.nBuilding Tree Structure.backslash.n" );
TreeNode root;
buildTree(&root,orders);
dumpTree(&root,1);
BitVector optOrder(numProcedures,TRUE);
double fee = getMinimumFee(&root,&optOrder);
printf(".backslash.n.backslash.nOptimum Order.backslash.n" );
dumpProcedureList(&optOrder);
return 0;
}
|
Same subclass Same class Consider this |
||||||||||
